Search
SailfishOS Open Build Service
>
Projects
>
home:abranson
:
branches:nemo:devel:hw:native-common
>
gstreamer1.0-plugins-base
> _service:tar_git:0000-Move-encodebin-sources-to-encodebasebin.patch
Log In
Username
Password
Cancel
Overview
Repositories
Revisions
Requests
Users
Advanced
Attributes
Meta
File _service:tar_git:0000-Move-encodebin-sources-to-encodebasebin.patch of Package gstreamer1.0-plugins-base
From 026b7f8328a3cf1c4e05f3f1a9520df151c9187a Fri, 3 May 2019 09:06:44 +0200 From: Andrew Branson <andrew.branson@jollamobile.com> Date: Fri, 3 May 2019 08:47:51 +0200 Subject: [PATCH] Move encodebin sources to encodebasebin diff --git a/gst/encoding/gstencodebasebin.c b/gst/encoding/gstencodebasebin.c new file mode 100644 index 0000000..6f8cc5e --- /dev/null +++ b/gst/encoding/gstencodebasebin.c @@ -0,0 +1,2351 @@ +/* GStreamer encoding bin + * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk> + * (C) 2009 Nokia Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include "gstencodebin.h" +#include "gstsmartencoder.h" +#include "gststreamsplitter.h" +#include "gststreamcombiner.h" +#include <gst/gst-i18n-plugin.h> + +/** + * SECTION:element-encodebin + * @title: encodebin + * + * EncodeBin provides a bin for encoding/muxing various streams according to + * a specified #GstEncodingProfile. + * + * Based on the profile that was set (via the #GstEncodeBin:profile property), + * EncodeBin will internally select and configure the required elements + * (encoders, muxers, but also audio and video converters) so that you can + * provide it raw or pre-encoded streams of data in input and have your + * encoded/muxed/converted stream in output. + * + * ## Features + * + * * Automatic encoder and muxer selection based on elements available on the + * system. + * + * * Conversion of raw audio/video streams (scaling, framerate conversion, + * colorspace conversion, samplerate conversion) to conform to the profile + * output format. + * + * * Variable number of streams. If the presence property for a stream encoding + * profile is 0, you can request any number of sink pads for it via the + * standard request pad gstreamer API or the #GstEncodeBin::request-pad action + * signal. + * + * * Avoid reencoding (passthrough). If the input stream is already encoded and is + * compatible with what the #GstEncodingProfile expects, then the stream won't + * be re-encoded but just passed through downstream to the muxer or the output. + * + * * Mix pre-encoded and raw streams as input. In addition to the passthrough + * feature above, you can feed both raw audio/video *AND* already-encoded data + * to a pad. #GstEncodeBin will take care of passing through the compatible + * segments and re-encoding the segments of media that need encoding. + * + * * Standard behaviour is to use a #GstEncodingContainerProfile to have both + * encoding and muxing performed. But you can also provide a single stream + * profile (like #GstEncodingAudioProfile) to only have the encoding done and + * handle the encoded output yourself. + * + * * Audio imperfection corrections. Incoming audio streams can have non perfect + * timestamps (jitter), like the streams coming from ASF files. #GstEncodeBin + * will automatically fix those imperfections for you. See + * #GstEncodeBin:audio-jitter-tolerance for more details. + * + * * Variable or Constant video framerate. If your #GstEncodingVideoProfile has + * the variableframerate property deactivated (default), then the incoming + * raw video stream will be retimestampped in order to produce a constant + * framerate. + * + * * Cross-boundary re-encoding. When feeding compatible pre-encoded streams that + * fall on segment boundaries, and for supported formats (right now only H263), + * the GOP will be decoded/reencoded when needed to produce an encoded output + * that fits exactly within the request GstSegment. + * + * * Missing plugin support. If a #GstElement is missing to encode/mux to the + * request profile formats, a missing-plugin #GstMessage will be posted on the + * #GstBus, allowing systems that support the missing-plugin system to offer the + * user a way to install the missing element. + * + */ + + +/* TODO/FIXME + * + * Handling mp3!xing!idv3 and theora!ogg tagsetting scenarios: + * Once we have chosen a muxer: + * When a new stream is requested: + * If muxer isn't 'Formatter' OR doesn't have a TagSetter interface: + * Find a Formatter for the given stream (preferably with TagSetter) + * Insert that before muxer + **/ + +#define fast_pad_link(a,b) gst_pad_link_full((a),(b),GST_PAD_LINK_CHECK_NOTHING) +#define fast_element_link(a,b) gst_element_link_pads_full((a),"src",(b),"sink",GST_PAD_LINK_CHECK_NOTHING) + +typedef enum +{ + GST_ENCODEBIN_FLAG_NO_AUDIO_CONVERSION = (1 << 0), + GST_ENCODEBIN_FLAG_NO_VIDEO_CONVERSION = (1 << 1) +} GstEncodeBinFlags; + +#define GST_TYPE_ENCODEBIN_FLAGS (gst_encodebin_flags_get_type()) +GType gst_encodebin_flags_get_type (void); + +/* generic templates */ +static GstStaticPadTemplate muxer_src_template = +GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate video_sink_template = +GST_STATIC_PAD_TEMPLATE ("video_%u", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS_ANY); +static GstStaticPadTemplate audio_sink_template = +GST_STATIC_PAD_TEMPLATE ("audio_%u", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS_ANY); +/* static GstStaticPadTemplate text_sink_template = */ +/* GST_STATIC_PAD_TEMPLATE ("text_%u", */ +/* GST_PAD_SINK, */ +/* GST_PAD_REQUEST, */ +/* GST_STATIC_CAPS_ANY); */ +static GstStaticPadTemplate private_sink_template = +GST_STATIC_PAD_TEMPLATE ("private_%u", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS_ANY); + +struct _GstEncodeBin +{ + GstBin parent; + + /* the profile field is only valid if it could be entirely setup */ + GstEncodingProfile *profile; + + GList *streams; /* List of StreamGroup, not sorted */ + + GstElement *muxer; + /* Ghostpad with changing target */ + GstPad *srcpad; + + /* TRUE if in PAUSED/PLAYING */ + gboolean active; + + /* available muxers, encoders and parsers */ + GList *muxers; + GList *formatters; + GList *encoders; + GList *parsers; + + /* Increasing counter for unique pad name */ + guint last_pad_id; + + /* Cached caps for identification */ + GstCaps *raw_video_caps; + GstCaps *raw_audio_caps; + /* GstCaps *raw_text_caps; */ + + guint queue_buffers_max; + guint queue_bytes_max; + guint64 queue_time_max; + + guint64 tolerance; + gboolean avoid_reencoding; + + GstEncodeBinFlags flags; +}; + +struct _GstEncodeBinClass +{ + GstBinClass parent; + + /* Action Signals */ + GstPad *(*request_pad) (GstEncodeBin * encodebin, GstCaps * caps); + GstPad *(*request_profile_pad) (GstEncodeBin * encodebin, + const gchar * profilename); +}; + +typedef struct _StreamGroup StreamGroup; + +struct _StreamGroup +{ + GstEncodeBin *ebin; + GstEncodingProfile *profile; + GstPad *ghostpad; /* Sink ghostpad */ + GstElement *inqueue; /* Queue just after the ghostpad */ + GstElement *splitter; + GList *converters; /* List of conversion GstElement */ + GstElement *capsfilter; /* profile->restriction (if non-NULL/ANY) */ + gulong inputfilter_caps_sid; + GstElement *encoder; /* Encoder (can be NULL) */ + GstElement *fakesink; /* Fakesink (can be NULL) */ + GstElement *combiner; + GstElement *parser; + GstElement *smartencoder; + GstElement *outfilter; /* Output capsfilter (streamprofile.format) */ + gulong outputfilter_caps_sid; + GstElement *formatter; + GstElement *outqueue; /* Queue just before the muxer */ + gulong restriction_sid; +}; + +/* Default for queues (same defaults as queue element) */ +#define DEFAULT_QUEUE_BUFFERS_MAX 200 +#define DEFAULT_QUEUE_BYTES_MAX 10 * 1024 * 1024 +#define DEFAULT_QUEUE_TIME_MAX GST_SECOND +#define DEFAULT_AUDIO_JITTER_TOLERANCE 20 * GST_MSECOND +#define DEFAULT_AVOID_REENCODING FALSE +#define DEFAULT_FLAGS 0 + +#define DEFAULT_RAW_CAPS \ + "video/x-raw; " \ + "audio/x-raw; " \ + "text/x-raw; " \ + "subpicture/x-dvd; " \ + "subpicture/x-pgs" + +/* Properties */ +enum +{ + PROP_0, + PROP_PROFILE, + PROP_QUEUE_BUFFERS_MAX, + PROP_QUEUE_BYTES_MAX, + PROP_QUEUE_TIME_MAX, + PROP_AUDIO_JITTER_TOLERANCE, + PROP_AVOID_REENCODING, + PROP_FLAGS +}; + +/* Signals */ +enum +{ + SIGNAL_REQUEST_PAD, + SIGNAL_REQUEST_PROFILE_PAD, + LAST_SIGNAL +}; + +#define C_FLAGS(v) ((guint) v) + +GType +gst_encodebin_flags_get_type (void) +{ + static const GFlagsValue values[] = { + {C_FLAGS (GST_ENCODEBIN_FLAG_NO_AUDIO_CONVERSION), "Do not use audio " + "conversion elements", "no-audio-conversion"}, + {C_FLAGS (GST_ENCODEBIN_FLAG_NO_VIDEO_CONVERSION), "Do not use video " + "conversion elements", "no-video-conversion"}, + {0, NULL, NULL} + }; + static volatile GType id = 0; + + if (g_once_init_enter ((gsize *) & id)) { + GType _id; + + _id = g_flags_register_static ("GstEncodeBinFlags", values); + + g_once_init_leave ((gsize *) & id, _id); + } + + return id; +} + +static guint gst_encode_bin_signals[LAST_SIGNAL] = { 0 }; + +static GstStaticCaps default_raw_caps = GST_STATIC_CAPS (DEFAULT_RAW_CAPS); + +GST_DEBUG_CATEGORY_STATIC (gst_encode_bin_debug); +#define GST_CAT_DEFAULT gst_encode_bin_debug + +G_DEFINE_TYPE (GstEncodeBin, gst_encode_bin, GST_TYPE_BIN); + +static void gst_encode_bin_dispose (GObject * object); +static void gst_encode_bin_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_encode_bin_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static GstStateChangeReturn gst_encode_bin_change_state (GstElement * element, + GstStateChange transition); + +static GstPad *gst_encode_bin_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name, const GstCaps * caps); +static void gst_encode_bin_release_pad (GstElement * element, GstPad * pad); + +static gboolean +gst_encode_bin_set_profile (GstEncodeBin * ebin, GstEncodingProfile * profile); +static void gst_encode_bin_tear_down_profile (GstEncodeBin * ebin); +static gboolean gst_encode_bin_setup_profile (GstEncodeBin * ebin, + GstEncodingProfile * profile); + +static StreamGroup *_create_stream_group (GstEncodeBin * ebin, + GstEncodingProfile * sprof, const gchar * sinkpadname, GstCaps * sinkcaps, + gboolean * encoder_not_found); +static void stream_group_remove (GstEncodeBin * ebin, StreamGroup * sgroup); +static void stream_group_free (GstEncodeBin * ebin, StreamGroup * sgroup); +static GstPad *gst_encode_bin_request_pad_signal (GstEncodeBin * encodebin, + GstCaps * caps); +static GstPad *gst_encode_bin_request_profile_pad_signal (GstEncodeBin * + encodebin, const gchar * profilename); + +static inline GstElement *_get_formatter (GstEncodeBin * ebin, + GstEncodingProfile * sprof); +static void _post_missing_plugin_message (GstEncodeBin * ebin, + GstEncodingProfile * prof); + +static void +gst_encode_bin_class_init (GstEncodeBinClass * klass) +{ + GObjectClass *gobject_klass; + GstElementClass *gstelement_klass; + + gobject_klass = (GObjectClass *) klass; + gstelement_klass = (GstElementClass *) klass; + + gobject_klass->dispose = gst_encode_bin_dispose; + gobject_klass->set_property = gst_encode_bin_set_property; + gobject_klass->get_property = gst_encode_bin_get_property; + + /* Properties */ + + /** + * GstEncodeBin:profile: + * + * The #GstEncodingProfile to use. This property must be set before going + * to %GST_STATE_PAUSED or higher. + */ + g_object_class_install_property (gobject_klass, PROP_PROFILE, + g_param_spec_object ("profile", "Profile", + "The GstEncodingProfile to use", GST_TYPE_ENCODING_PROFILE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_klass, PROP_QUEUE_BYTES_MAX, + g_param_spec_uint ("queue-bytes-max", "Max. size (kB)", + "Max. amount of data in the queue (bytes, 0=disable)", + 0, G_MAXUINT, DEFAULT_QUEUE_BYTES_MAX, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_klass, PROP_QUEUE_BUFFERS_MAX, + g_param_spec_uint ("queue-buffers-max", "Max. size (buffers)", + "Max. number of buffers in the queue (0=disable)", 0, G_MAXUINT, + DEFAULT_QUEUE_BUFFERS_MAX, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_klass, PROP_QUEUE_TIME_MAX, + g_param_spec_uint64 ("queue-time-max", "Max. size (ns)", + "Max. amount of data in the queue (in ns, 0=disable)", 0, G_MAXUINT64, + DEFAULT_QUEUE_TIME_MAX, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_klass, PROP_AUDIO_JITTER_TOLERANCE, + g_param_spec_uint64 ("audio-jitter-tolerance", "Audio jitter tolerance", + "Amount of timestamp jitter/imperfection to allow on audio streams before inserting/dropping samples (ns)", + 0, G_MAXUINT64, DEFAULT_AUDIO_JITTER_TOLERANCE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_klass, PROP_AVOID_REENCODING, + g_param_spec_boolean ("avoid-reencoding", "Avoid re-encoding", + "Whether to re-encode portions of compatible video streams that lay on segment boundaries", + DEFAULT_AVOID_REENCODING, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GstEncodeBin:flags + * + * Control the behaviour of encodebin. + */ + g_object_class_install_property (gobject_klass, PROP_FLAGS, + g_param_spec_flags ("flags", "Flags", "Flags to control behaviour", + GST_TYPE_ENCODEBIN_FLAGS, DEFAULT_FLAGS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /* Signals */ + /** + * GstEncodeBin::request-pad + * @encodebin: a #GstEncodeBin instance + * @caps: a #GstCaps + * + * Use this method to request an unused sink request #GstPad that can take the + * provided @caps as input. You must release the pad with + * gst_element_release_request_pad() when you are done with it. + * + * Returns: A compatible #GstPad, or %NULL if no compatible #GstPad could be + * created or is available. + */ + gst_encode_bin_signals[SIGNAL_REQUEST_PAD] = + g_signal_new ("request-pad", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstEncodeBinClass, + request_pad), NULL, NULL, g_cclosure_marshal_generic, + GST_TYPE_PAD, 1, GST_TYPE_CAPS); + + /** + * GstEncodeBin::request-profile-pad + * @encodebin: a #GstEncodeBin instance + * @profilename: the name of a #GstEncodingProfile + * + * Use this method to request an unused sink request #GstPad from the profile + * @profilename. You must release the pad with + * gst_element_release_request_pad() when you are done with it. + * + * Returns: A compatible #GstPad, or %NULL if no compatible #GstPad could be + * created or is available. + */ + gst_encode_bin_signals[SIGNAL_REQUEST_PROFILE_PAD] = + g_signal_new ("request-profile-pad", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstEncodeBinClass, + request_profile_pad), NULL, NULL, g_cclosure_marshal_generic, + GST_TYPE_PAD, 1, G_TYPE_STRING); + + klass->request_pad = gst_encode_bin_request_pad_signal; + klass->request_profile_pad = gst_encode_bin_request_profile_pad_signal; + + gst_element_class_add_static_pad_template (gstelement_klass, + &muxer_src_template); + gst_element_class_add_static_pad_template (gstelement_klass, + &video_sink_template); + gst_element_class_add_static_pad_template (gstelement_klass, + &audio_sink_template); + /* gst_element_class_add_static_pad_template (gstelement_klass, &text_sink_template); */ + gst_element_class_add_static_pad_template (gstelement_klass, + &private_sink_template); + + gstelement_klass->change_state = + GST_DEBUG_FUNCPTR (gst_encode_bin_change_state); + gstelement_klass->request_new_pad = + GST_DEBUG_FUNCPTR (gst_encode_bin_request_new_pad); + gstelement_klass->release_pad = + GST_DEBUG_FUNCPTR (gst_encode_bin_release_pad); + + gst_element_class_set_static_metadata (gstelement_klass, + "Encoder Bin", + "Generic/Bin/Encoder", + "Convenience encoding/muxing element", + "Edward Hervey <edward.hervey@collabora.co.uk>"); +} + +static void +gst_encode_bin_dispose (GObject * object) +{ + GstEncodeBin *ebin = (GstEncodeBin *) object; + + if (ebin->muxers) + gst_plugin_feature_list_free (ebin->muxers); + ebin->muxers = NULL; + + if (ebin->formatters) + gst_plugin_feature_list_free (ebin->formatters); + ebin->formatters = NULL; + + if (ebin->encoders) + gst_plugin_feature_list_free (ebin->encoders); + ebin->encoders = NULL; + + if (ebin->parsers) + gst_plugin_feature_list_free (ebin->parsers); + ebin->parsers = NULL; + + gst_encode_bin_tear_down_profile (ebin); + + if (ebin->raw_video_caps) + gst_caps_unref (ebin->raw_video_caps); + ebin->raw_video_caps = NULL; + if (ebin->raw_audio_caps) + gst_caps_unref (ebin->raw_audio_caps); + ebin->raw_audio_caps = NULL; + /* if (ebin->raw_text_caps) */ + /* gst_caps_unref (ebin->raw_text_caps); */ + + G_OBJECT_CLASS (gst_encode_bin_parent_class)->dispose (object); +} + +static void +gst_encode_bin_init (GstEncodeBin * encode_bin) +{ + GstPadTemplate *tmpl; + + encode_bin->muxers = + gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_MUXER, + GST_RANK_MARGINAL); + + encode_bin->formatters = + gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_FORMATTER, + GST_RANK_SECONDARY); + + encode_bin->encoders = + gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_ENCODER, + GST_RANK_MARGINAL); + + encode_bin->parsers = + gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_PARSER, + GST_RANK_MARGINAL); + + encode_bin->raw_video_caps = gst_caps_from_string ("video/x-raw"); + encode_bin->raw_audio_caps = gst_caps_from_string ("audio/x-raw"); + /* encode_bin->raw_text_caps = */ + /* gst_caps_from_string ("text/x-raw"); */ + + encode_bin->queue_buffers_max = DEFAULT_QUEUE_BUFFERS_MAX; + encode_bin->queue_bytes_max = DEFAULT_QUEUE_BYTES_MAX; + encode_bin->queue_time_max = DEFAULT_QUEUE_TIME_MAX; + encode_bin->tolerance = DEFAULT_AUDIO_JITTER_TOLERANCE; + encode_bin->avoid_reencoding = DEFAULT_AVOID_REENCODING; + encode_bin->flags = DEFAULT_FLAGS; + + tmpl = gst_static_pad_template_get (&muxer_src_template); + encode_bin->srcpad = gst_ghost_pad_new_no_target_from_template ("src", tmpl); + gst_object_unref (tmpl); + gst_pad_set_active (encode_bin->srcpad, TRUE); + gst_element_add_pad (GST_ELEMENT_CAST (encode_bin), encode_bin->srcpad); +} + +static void +gst_encode_bin_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstEncodeBin *ebin = (GstEncodeBin *) object; + + switch (prop_id) { + case PROP_PROFILE: + gst_encode_bin_set_profile (ebin, + (GstEncodingProfile *) g_value_get_object (value)); + break; + case PROP_QUEUE_BUFFERS_MAX: + ebin->queue_buffers_max = g_value_get_uint (value); + break; + case PROP_QUEUE_BYTES_MAX: + ebin->queue_bytes_max = g_value_get_uint (value); + break; + case PROP_QUEUE_TIME_MAX: + ebin->queue_time_max = g_value_get_uint64 (value); + break; + case PROP_AUDIO_JITTER_TOLERANCE: + ebin->tolerance = g_value_get_uint64 (value); + break; + case PROP_AVOID_REENCODING: + ebin->avoid_reencoding = g_value_get_boolean (value); + break; + case PROP_FLAGS: + ebin->flags = g_value_get_flags (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_encode_bin_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstEncodeBin *ebin = (GstEncodeBin *) object; + + switch (prop_id) { + case PROP_PROFILE: + g_value_set_object (value, (GObject *) ebin->profile); + break; + case PROP_QUEUE_BUFFERS_MAX: + g_value_set_uint (value, ebin->queue_buffers_max); + break; + case PROP_QUEUE_BYTES_MAX: + g_value_set_uint (value, ebin->queue_bytes_max); + break; + case PROP_QUEUE_TIME_MAX: + g_value_set_uint64 (value, ebin->queue_time_max); + break; + case PROP_AUDIO_JITTER_TOLERANCE: + g_value_set_uint64 (value, ebin->tolerance); + break; + case PROP_AVOID_REENCODING: + g_value_set_boolean (value, ebin->avoid_reencoding); + break; + case PROP_FLAGS: + g_value_set_flags (value, ebin->flags); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static inline gboolean +are_raw_caps (const GstCaps * caps) +{ + GstCaps *raw = gst_static_caps_get (&default_raw_caps); + gboolean res = gst_caps_can_intersect (caps, raw); + + gst_caps_unref (raw); + return res; +} + +/* Returns the number of time a given stream profile is currently used + * in encodebin */ +static inline guint +stream_profile_used_count (GstEncodeBin * ebin, GstEncodingProfile * sprof) +{ + guint nbprofused = 0; + GList *tmp; + + for (tmp = ebin->streams; tmp; tmp = tmp->next) { + StreamGroup *sgroup = (StreamGroup *) tmp->data; + + if (sgroup->profile == sprof) + nbprofused++; + } + + return nbprofused; +} + +static inline GstEncodingProfile * +next_unused_stream_profile (GstEncodeBin * ebin, GType ptype, + const gchar * name, GstCaps * caps, GstEncodingProfile * previous_profile) +{ + GST_DEBUG_OBJECT (ebin, "ptype:%s, caps:%" GST_PTR_FORMAT, + g_type_name (ptype), caps); + + if (G_UNLIKELY (ptype == G_TYPE_NONE && caps != NULL)) { + /* Identify the profile type based on raw caps */ + if (gst_caps_can_intersect (ebin->raw_video_caps, caps)) + ptype = GST_TYPE_ENCODING_VIDEO_PROFILE; + else if (gst_caps_can_intersect (ebin->raw_audio_caps, caps)) + ptype = GST_TYPE_ENCODING_AUDIO_PROFILE; + /* else if (gst_caps_can_intersect (ebin->raw_text_caps, caps)) */ + /* ptype = GST_TYPE_ENCODING_TEXT_PROFILE; */ + GST_DEBUG_OBJECT (ebin, "Detected profile type as being %s", + g_type_name (ptype)); + } + + if (GST_IS_ENCODING_CONTAINER_PROFILE (ebin->profile)) { + const GList *tmp; + + if (name) { + /* If we have a name, try to find a profile with the same name */ + tmp = + gst_encoding_container_profile_get_profiles + (GST_ENCODING_CONTAINER_PROFILE (ebin->profile)); + + for (; tmp; tmp = tmp->next) { + GstEncodingProfile *sprof = (GstEncodingProfile *) tmp->data; + const gchar *profilename = gst_encoding_profile_get_name (sprof); + + if (profilename && !strcmp (name, profilename)) { + guint presence = gst_encoding_profile_get_presence (sprof); + + GST_DEBUG ("Found profile matching the requested name"); + + if (!gst_encoding_profile_is_enabled (sprof)) { + GST_INFO_OBJECT (ebin, "%p is disabled, not using it", sprof); + + return NULL; + } + + if (presence == 0 + || presence > stream_profile_used_count (ebin, sprof)) + return sprof; + + GST_WARNING ("Matching stream already used"); + return NULL; + } + } + GST_DEBUG + ("No profiles matching requested pad name, carrying on with normal stream matching"); + } + + for (tmp = + gst_encoding_container_profile_get_profiles + (GST_ENCODING_CONTAINER_PROFILE (ebin->profile)); tmp; + tmp = tmp->next) { + GstEncodingProfile *sprof = (GstEncodingProfile *) tmp->data; + + /* Pick an available Stream profile for which: + * * either it is of the compatible raw type, + * * OR we can pass it through directly without encoding + */ + if (G_TYPE_FROM_INSTANCE (sprof) == ptype) { + guint presence = gst_encoding_profile_get_presence (sprof); + GST_DEBUG ("Found a stream profile with the same type"); + if (!gst_encoding_profile_is_enabled (sprof)) { + GST_INFO_OBJECT (ebin, "%p is disabled, not using it", sprof); + } else if (presence == 0 + || (presence > stream_profile_used_count (ebin, sprof))) { + + if (sprof != previous_profile) + return sprof; + } + } else if (caps && ptype == G_TYPE_NONE) { + GstCaps *outcaps; + gboolean res; + + outcaps = gst_encoding_profile_get_input_caps (sprof); + GST_DEBUG ("Unknown stream, seeing if it's compatible with %" + GST_PTR_FORMAT, outcaps); + res = gst_caps_can_intersect (outcaps, caps); + gst_caps_unref (outcaps); + + if (res && sprof != previous_profile) + return sprof; + } + } + } + + return NULL; +} + +static GstPad * +request_pad_for_stream (GstEncodeBin * encodebin, GType ptype, + const gchar * name, GstCaps * caps) +{ + StreamGroup *sgroup = NULL; + GList *not_found_encoder_profs = NULL, *tmp; + GstEncodingProfile *sprof = NULL; + + GST_DEBUG_OBJECT (encodebin, "name:%s caps:%" GST_PTR_FORMAT, name, caps); + + while (sgroup == NULL) { + gboolean encoder_not_found = FALSE; + /* Figure out if we have a unused GstEncodingProfile we can use for + * these caps */ + sprof = next_unused_stream_profile (encodebin, ptype, name, caps, sprof); + + if (G_UNLIKELY (sprof == NULL)) + goto no_stream_profile; + + sgroup = _create_stream_group (encodebin, sprof, name, caps, + &encoder_not_found); + + if (G_UNLIKELY (sgroup)) + break; + + if (encoder_not_found) { + not_found_encoder_profs = g_list_prepend (not_found_encoder_profs, sprof); + if (name) { + GST_DEBUG ("Could not create an encoder for %s", name); + goto no_stream_group; + } + } else { + break; + } + } + + if (!sgroup) + goto no_stream_group; + + g_list_free (not_found_encoder_profs); + return sgroup->ghostpad; + +no_stream_profile: + { + GST_WARNING_OBJECT (encodebin, "Couldn't find a compatible stream profile"); + return NULL; + } + +no_stream_group: + { + for (tmp = not_found_encoder_profs; tmp; tmp = tmp->next) + _post_missing_plugin_message (encodebin, tmp->data); + g_list_free (not_found_encoder_profs); + + GST_WARNING_OBJECT (encodebin, "Couldn't create a StreamGroup"); + return NULL; + } +} + +static GstPad * +gst_encode_bin_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * name, const GstCaps * caps) +{ + GstEncodeBin *ebin = (GstEncodeBin *) element; + GstPad *res = NULL; + + GST_DEBUG_OBJECT (element, "templ:%s, name:%s", templ->name_template, name); + + /* Identify the stream group (if name or caps have been provided) */ + if (caps != NULL || name != NULL) { + res = request_pad_for_stream (ebin, G_TYPE_NONE, name, (GstCaps *) caps); + } + + if (res == NULL) { + GType ptype = G_TYPE_NONE; + + if (!strcmp (templ->name_template, "video_%u")) + ptype = GST_TYPE_ENCODING_VIDEO_PROFILE; + else if (!strcmp (templ->name_template, "audio_%u")) + ptype = GST_TYPE_ENCODING_AUDIO_PROFILE; + /* else if (!strcmp (templ->name_template, "text_%u")) */ + /* ptype = GST_TYPE_ENCODING_TEXT_PROFILE; */ + + /* FIXME : Check uniqueness of pad */ + /* FIXME : Check that the requested number is the last one, and if not, + * update the last_pad_id variable so that we don't create a pad with + * the same name/number in the future */ + + res = request_pad_for_stream (ebin, ptype, name, NULL); + } + + return res; +} + +static GstPad * +gst_encode_bin_request_pad_signal (GstEncodeBin * encodebin, GstCaps * caps) +{ + GstPad *pad = request_pad_for_stream (encodebin, G_TYPE_NONE, NULL, caps); + + return pad ? GST_PAD_CAST (gst_object_ref (pad)) : NULL; +} + +static GstPad * +gst_encode_bin_request_profile_pad_signal (GstEncodeBin * encodebin, + const gchar * profilename) +{ + GstPad *pad = + request_pad_for_stream (encodebin, G_TYPE_NONE, profilename, NULL); + + return pad ? GST_PAD_CAST (gst_object_ref (pad)) : NULL; +} + +static inline StreamGroup * +find_stream_group_from_pad (GstEncodeBin * ebin, GstPad * pad) +{ + GList *tmp; + + for (tmp = ebin->streams; tmp; tmp = tmp->next) { + StreamGroup *sgroup = (StreamGroup *) tmp->data; + if (G_UNLIKELY (sgroup->ghostpad == pad)) + return sgroup; + } + + return NULL; +} + +static void +gst_encode_bin_release_pad (GstElement * element, GstPad * pad) +{ + GstEncodeBin *ebin = (GstEncodeBin *) element; + StreamGroup *sgroup; + + /* Find the associated StreamGroup */ + + sgroup = find_stream_group_from_pad (ebin, pad); + if (G_UNLIKELY (sgroup == NULL)) + goto no_stream_group; + + /* Release objects/data associated with the StreamGroup */ + stream_group_remove (ebin, sgroup); + + return; + +no_stream_group: + { + GST_WARNING_OBJECT (ebin, "Couldn't find corresponding StreamGroup"); + return; + } +} + +/* Create a parser for the given stream profile */ +static inline GstElement * +_get_parser (GstEncodeBin * ebin, GstEncodingProfile * sprof) +{ + GList *parsers1, *parsers, *tmp; + GstElement *parser = NULL; + GstElementFactory *parserfact = NULL; + GstCaps *format; + + format = gst_encoding_profile_get_format (sprof); + + GST_DEBUG ("Getting list of parsers for format %" GST_PTR_FORMAT, format); + + /* FIXME : requesting twice the parsers twice is a bit ugly, we should + * have a method to request on more than one condition */ + parsers1 = + gst_element_factory_list_filter (ebin->parsers, format, + GST_PAD_SRC, FALSE); + parsers = + gst_element_factory_list_filter (parsers1, format, GST_PAD_SINK, FALSE); + gst_plugin_feature_list_free (parsers1); + + if (G_UNLIKELY (parsers == NULL)) { + GST_DEBUG ("Couldn't find any compatible parsers"); + goto beach; + } + + for (tmp = parsers; tmp; tmp = tmp->next) { + /* FIXME : We're only picking the first one so far */ + /* FIXME : signal the user if he wants this */ + parserfact = (GstElementFactory *) tmp->data; + break; + } + + if (parserfact) + parser = gst_element_factory_create (parserfact, NULL); + + gst_plugin_feature_list_free (parsers); + +beach: + if (format) + gst_caps_unref (format); + + return parser; +} + +static GstElement * +_create_element_and_set_preset (GstElementFactory * factory, + const gchar * preset, const gchar * name, const gchar * preset_name) +{ + GstElement *res = NULL; + + GST_DEBUG ("Creating element from factory %s (preset factory name: %s" + " preset name: %s)", GST_OBJECT_NAME (factory), preset_name, preset); + + if (preset_name && g_strcmp0 (GST_OBJECT_NAME (factory), preset_name)) { + GST_DEBUG ("Got to use %s, not %s", preset_name, GST_OBJECT_NAME (factory)); + return NULL; + } + + res = gst_element_factory_create (factory, name); + + if (preset && GST_IS_PRESET (res)) { + if (preset_name == NULL || + g_strcmp0 (GST_OBJECT_NAME (factory), preset_name) == 0) { + + if (!gst_preset_load_preset (GST_PRESET (res), preset)) { + GST_WARNING ("Couldn't set preset [%s] on element [%s]", + preset, GST_OBJECT_NAME (factory)); + gst_object_unref (res); + res = NULL; + } + } else { + GST_DEBUG ("Using a preset with no preset name, making use of the" + " proper element without setting any property"); + } + } + /* Else we keep it */ + + return res; +} + +/* Create the encoder for the given stream profile */ +static inline GstElement * +_get_encoder (GstEncodeBin * ebin, GstEncodingProfile * sprof) +{ + GList *encoders, *tmp; + GstElement *encoder = NULL; + GstElementFactory *encoderfact = NULL; + GstCaps *format; + const gchar *preset, *preset_name; + + format = gst_encoding_profile_get_format (sprof); + preset = gst_encoding_profile_get_preset (sprof); + preset_name = gst_encoding_profile_get_preset_name (sprof); + + GST_DEBUG ("Getting list of encoders for format %" GST_PTR_FORMAT, format); + + /* If stream caps are raw, return identity */ + if (G_UNLIKELY (are_raw_caps (format))) { + GST_DEBUG ("Stream format is raw, returning identity as the encoder"); + encoder = gst_element_factory_make ("identity", NULL); + goto beach; + } + + encoders = + gst_element_factory_list_filter (ebin->encoders, format, + GST_PAD_SRC, FALSE); + + if (G_UNLIKELY (encoders == NULL) && sprof == ebin->profile) { + /* Special case: if the top-level profile is an encoder, + * it could be listed in our muxers (for example wavenc) + */ + encoders = gst_element_factory_list_filter (ebin->muxers, format, + GST_PAD_SRC, FALSE); + } + + if (G_UNLIKELY (encoders == NULL)) { + GST_DEBUG ("Couldn't find any compatible encoders"); + goto beach; + } + + for (tmp = encoders; tmp; tmp = tmp->next) { + encoderfact = (GstElementFactory *) tmp->data; + if ((encoder = _create_element_and_set_preset (encoderfact, preset, + NULL, preset_name))) + break; + } + + gst_plugin_feature_list_free (encoders); + +beach: + if (format) + gst_caps_unref (format); + + return encoder; +} + +static GstPad * +local_element_request_pad (GstElement * element, GstPadTemplate * templ, + const gchar * name, const GstCaps * caps) +{ + GstPad *newpad = NULL; + GstElementClass *oclass; + + oclass = GST_ELEMENT_GET_CLASS (element); + + if (oclass->request_new_pad) + newpad = (oclass->request_new_pad) (element, templ, name, caps); + + if (newpad) + gst_object_ref (newpad); + + return newpad; +} + +static GstPad * +gst_element_get_pad_from_template (GstElement * element, GstPadTemplate * templ) +{ + GstPad *ret = NULL; + GstPadPresence presence; + + /* If this function is ever exported, we need check the validity of `element' + * and `templ', and to make sure the template actually belongs to the + * element. */ + + presence = GST_PAD_TEMPLATE_PRESENCE (templ); + + switch (presence) { + case GST_PAD_ALWAYS: + case GST_PAD_SOMETIMES: + ret = gst_element_get_static_pad (element, templ->name_template); + if (!ret && presence == GST_PAD_ALWAYS) + g_warning + ("Element %s has an ALWAYS template %s, but no pad of the same name", + GST_OBJECT_NAME (element), templ->name_template); + break; + + case GST_PAD_REQUEST: + ret = gst_element_request_pad (element, templ, NULL, NULL); + break; + } + + return ret; +} + +/* FIXME : Improve algorithm for finding compatible muxer sink pad */ +static inline GstPad * +get_compatible_muxer_sink_pad (GstEncodeBin * ebin, GstElement * encoder, + GstCaps * sinkcaps) +{ + GstPad *sinkpad; + GstPadTemplate *srctempl = NULL; + GstPadTemplate *sinktempl; + + if (encoder) { + GstPad *srcpad; + srcpad = gst_element_get_static_pad (encoder, "src"); + + srctempl = gst_pad_get_pad_template (srcpad); + + GST_DEBUG_OBJECT (ebin, + "Attempting to find pad from muxer %s compatible with %s:%s", + GST_ELEMENT_NAME (ebin->muxer), GST_DEBUG_PAD_NAME (srcpad)); + + gst_object_unref (srcpad); + sinktempl = gst_element_get_compatible_pad_template (ebin->muxer, srctempl); + gst_object_unref (srctempl); + } else { + srctempl = + gst_pad_template_new ("whatever", GST_PAD_SRC, GST_PAD_ALWAYS, + sinkcaps); + g_assert (srctempl != NULL); + sinktempl = gst_element_get_compatible_pad_template (ebin->muxer, srctempl); + gst_object_unref (srctempl); + } + + if (G_UNLIKELY (sinktempl == NULL)) + goto no_template; + + sinkpad = gst_element_get_pad_from_template (ebin->muxer, sinktempl); + + return sinkpad; + +no_template: + { + GST_WARNING_OBJECT (ebin, "No compatible pad available on muxer"); + return NULL; + } +} + +static gboolean +_has_class (GstElement * element, const gchar * classname) +{ + GstElementClass *klass; + const gchar *value; + + klass = GST_ELEMENT_GET_CLASS (element); + value = gst_element_class_get_metadata (klass, GST_ELEMENT_METADATA_KLASS); + if (!value) + return FALSE; + + return strstr (value, classname) != NULL; +} + +static void +_profile_restriction_caps_cb (GstEncodingProfile * profile, + GParamSpec * arg G_GNUC_UNUSED, StreamGroup * group) +{ + GstCaps *restriction = gst_encoding_profile_get_restriction (profile); + + g_object_set (group->capsfilter, "caps", restriction, NULL); +} + +static void +_capsfilter_force_format (GstPad * pad, + GParamSpec * arg G_GNUC_UNUSED, gulong * signal_id) +{ + GstCaps *caps; + GstStructure *structure; + + g_object_get (pad, "caps", &caps, NULL); + caps = gst_caps_copy (caps); + + structure = gst_caps_get_structure (caps, 0); + gst_structure_remove_field (structure, "streamheader"); + GST_INFO_OBJECT (pad, "Forcing caps to %" GST_PTR_FORMAT, caps); + g_object_set (GST_OBJECT_PARENT (pad), "caps", caps, NULL); + g_signal_handler_disconnect (pad, *signal_id); + *signal_id = 0; + gst_caps_unref (caps); +} + +static void +_set_group_caps_format (StreamGroup * sgroup, GstEncodingProfile * prof, + GstCaps * format) +{ + g_object_set (sgroup->outfilter, "caps", format, NULL); + + if (!gst_encoding_profile_get_allow_dynamic_output (prof)) { + if (!sgroup->outputfilter_caps_sid) { + sgroup->outputfilter_caps_sid = + g_signal_connect (sgroup->outfilter->sinkpads->data, + "notify::caps", G_CALLBACK (_capsfilter_force_format), + &sgroup->outputfilter_caps_sid); + } + } +} + +static void +_post_missing_plugin_message (GstEncodeBin * ebin, GstEncodingProfile * prof) +{ + GstCaps *format; + format = gst_encoding_profile_get_format (prof); + + GST_ERROR_OBJECT (ebin, + "Couldn't create encoder with preset %s and preset name %s" + " for format %" GST_PTR_FORMAT, + GST_STR_NULL (gst_encoding_profile_get_preset (prof)), + GST_STR_NULL (gst_encoding_profile_get_preset_name (prof)), format); + + /* missing plugin support */ + gst_element_post_message (GST_ELEMENT_CAST (ebin), + gst_missing_encoder_message_new (GST_ELEMENT_CAST (ebin), format)); + GST_ELEMENT_ERROR (ebin, CORE, MISSING_PLUGIN, + ("Couldn't create encoder for format %" GST_PTR_FORMAT, format), (NULL)); + + gst_caps_unref (format); +} + +static GstPadProbeReturn +_missing_plugin_probe (GstPad * pad, GstPadProbeInfo * info, gpointer udata) +{ + StreamGroup *sgroup = udata; + GstEncodeBin *ebin = sgroup->ebin; + + _post_missing_plugin_message (ebin, sgroup->profile); + + return GST_PAD_PROBE_OK; +} + +static void +_set_up_fake_encoder_pad_probe (GstEncodeBin * ebin, StreamGroup * sgroup) +{ + GstPad *pad = gst_element_get_static_pad (sgroup->fakesink, "sink"); + + gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER, _missing_plugin_probe, + sgroup, NULL); + + gst_object_unref (pad); +} + +/* FIXME : Add handling of streams that don't require conversion elements */ +/* + * Create the elements, StreamGroup, add the sink pad, link it to the muxer + * + * sinkpadname: If non-NULL, that name will be assigned to the sink ghost pad + * sinkcaps: If non-NULL will be used to figure out how to setup the group + * encoder_not_found: If non NULL, set to TRUE if failure happened because + * the encoder could not be found + */ +static StreamGroup * +_create_stream_group (GstEncodeBin * ebin, GstEncodingProfile * sprof, + const gchar * sinkpadname, GstCaps * sinkcaps, gboolean * encoder_not_found) +{ + StreamGroup *sgroup = NULL; + GstPad *sinkpad, *srcpad = NULL, *muxerpad = NULL; + /* Element we will link to the encoder */ + GstElement *last = NULL; + GstElement *encoder = NULL; + GList *tmp, *tosync = NULL; + GstCaps *format, *restriction; + const gchar *missing_element_name; + + format = gst_encoding_profile_get_format (sprof); + restriction = gst_encoding_profile_get_restriction (sprof); + + GST_DEBUG ("Creating group. format %" GST_PTR_FORMAT ", for caps %" + GST_PTR_FORMAT, format, sinkcaps); + GST_DEBUG ("avoid_reencoding:%d", ebin->avoid_reencoding); + + sgroup = g_slice_new0 (StreamGroup); + sgroup->ebin = ebin; + sgroup->profile = sprof; + + /* NOTE for people reading this code: + * + * We construct the group starting by the furthest downstream element + * and making our way up adding/syncing/linking as we go. + * + * There are two parallel paths: + * * One for raw data which goes through converters and encoders + * * One for already encoded data + */ + + /* Muxer. + * If we are handling a container profile, figure out if the muxer has a + * sinkpad compatible with the selected profile */ + if (ebin->muxer) { + muxerpad = get_compatible_muxer_sink_pad (ebin, NULL, format); + if (G_UNLIKELY (muxerpad == NULL)) + goto no_muxer_pad; + + } + + /* Output Queue. + * The actual queueing will be done in the input queue, but some queuing + * after the encoder can be beneficial for encoding performance. */ + last = sgroup->outqueue = gst_element_factory_make ("queue", NULL); + g_object_set (sgroup->outqueue, "max-size-buffers", (guint) 0, + "max-size-bytes", (guint) 0, "max-size-time", (guint64) 3 * GST_SECOND, + "silent", TRUE, NULL); + + gst_bin_add (GST_BIN (ebin), sgroup->outqueue); + tosync = g_list_append (tosync, sgroup->outqueue); + srcpad = gst_element_get_static_pad (sgroup->outqueue, "src"); + if (muxerpad) { + if (G_UNLIKELY (fast_pad_link (srcpad, muxerpad) != GST_PAD_LINK_OK)) { + goto muxer_link_failure; + } + gst_object_unref (muxerpad); + } else { + gst_ghost_pad_set_target (GST_GHOST_PAD (ebin->srcpad), srcpad); + } + gst_object_unref (srcpad); + srcpad = NULL; + + /* Check if we need a formatter + * If we have no muxer or + * if the muxer isn't a formatter and doesn't implement the tagsetter interface + */ + if (!ebin->muxer || (!GST_IS_TAG_SETTER (ebin->muxer) + && !_has_class (ebin->muxer, "Formatter"))) { + sgroup->formatter = _get_formatter (ebin, sprof); + if (sgroup->formatter) { + GST_DEBUG ("Adding formatter for %" GST_PTR_FORMAT, format); + + gst_bin_add (GST_BIN (ebin), sgroup->formatter); + tosync = g_list_append (tosync, sgroup->formatter); + if (G_UNLIKELY (!fast_element_link (sgroup->formatter, last))) + goto formatter_link_failure; + last = sgroup->formatter; + } + } + + + /* Output capsfilter + * This will receive the format caps from the streamprofile */ + GST_DEBUG ("Adding output capsfilter for %" GST_PTR_FORMAT, format); + sgroup->outfilter = gst_element_factory_make ("capsfilter", NULL); + _set_group_caps_format (sgroup, sprof, format); + + gst_bin_add (GST_BIN (ebin), sgroup->outfilter); + tosync = g_list_append (tosync, sgroup->outfilter); + if (G_UNLIKELY (!fast_element_link (sgroup->outfilter, last))) + goto outfilter_link_failure; + last = sgroup->outfilter; + + + sgroup->parser = _get_parser (ebin, sprof); + + if (sgroup->parser != NULL) { + GST_DEBUG ("Got a parser %s", GST_ELEMENT_NAME (sgroup->parser)); + gst_bin_add (GST_BIN (ebin), sgroup->parser); + tosync = g_list_append (tosync, sgroup->parser); + if (G_UNLIKELY (!gst_element_link (sgroup->parser, last))) + goto parser_link_failure; + last = sgroup->parser; + } + + /* Stream combiner */ + sgroup->combiner = g_object_new (GST_TYPE_STREAM_COMBINER, NULL); + + gst_bin_add (GST_BIN (ebin), sgroup->combiner); + tosync = g_list_append (tosync, sgroup->combiner); + if (G_UNLIKELY (!fast_element_link (sgroup->combiner, last))) + goto combiner_link_failure; + + + /* Stream splitter */ + sgroup->splitter = g_object_new (GST_TYPE_STREAM_SPLITTER, NULL); + + gst_bin_add (GST_BIN (ebin), sgroup->splitter); + tosync = g_list_append (tosync, sgroup->splitter); + + /* Input queue + * FIXME : figure out what max-size to use for the input queue */ + sgroup->inqueue = gst_element_factory_make ("queue", NULL); + g_object_set (sgroup->inqueue, "max-size-buffers", + (guint) ebin->queue_buffers_max, "max-size-bytes", + (guint) ebin->queue_bytes_max, "max-size-time", + (guint64) ebin->queue_time_max, "silent", TRUE, NULL); + + gst_bin_add (GST_BIN (ebin), sgroup->inqueue); + tosync = g_list_append (tosync, sgroup->inqueue); + if (G_UNLIKELY (!fast_element_link (sgroup->inqueue, sgroup->splitter))) + goto splitter_link_failure; + + /* Expose input queue sink pad as ghostpad */ + sinkpad = gst_element_get_static_pad (sgroup->inqueue, "sink"); + if (sinkpadname == NULL) { + gchar *pname = + g_strdup_printf ("%s_%u", gst_encoding_profile_get_type_nick (sprof), + ebin->last_pad_id++); + GST_DEBUG ("Adding ghost pad %s", pname); + sgroup->ghostpad = gst_ghost_pad_new (pname, sinkpad); + g_free (pname); + } else + sgroup->ghostpad = gst_ghost_pad_new (sinkpadname, sinkpad); + gst_object_unref (sinkpad); + + + /* Path 1 : Already-encoded data */ + sinkpad = + local_element_request_pad (sgroup->combiner, NULL, "passthroughsink", + NULL); + if (G_UNLIKELY (sinkpad == NULL)) + goto no_combiner_sinkpad; + + if (ebin->avoid_reencoding) { + GstCaps *tmpcaps; + + GST_DEBUG ("Asked to use Smart Encoder"); + sgroup->smartencoder = g_object_new (GST_TYPE_SMART_ENCODER, NULL); + + /* Check if stream format is compatible */ + srcpad = gst_element_get_static_pad (sgroup->smartencoder, "src"); + tmpcaps = gst_pad_query_caps (srcpad, NULL); + if (!gst_caps_can_intersect (tmpcaps, format)) { + GST_DEBUG ("We don't have a smart encoder for the stream format"); + gst_object_unref (sgroup->smartencoder); + sgroup->smartencoder = NULL; + } else { + gst_bin_add ((GstBin *) ebin, sgroup->smartencoder); + fast_pad_link (srcpad, sinkpad); + tosync = g_list_append (tosync, sgroup->smartencoder); + sinkpad = gst_element_get_static_pad (sgroup->smartencoder, "sink"); + } + gst_caps_unref (tmpcaps); + gst_object_unref (srcpad); + } + + srcpad = + local_element_request_pad (sgroup->splitter, NULL, "passthroughsrc", + NULL); + if (G_UNLIKELY (srcpad == NULL)) + goto no_splitter_srcpad; + + /* Go straight to splitter */ + if (G_UNLIKELY (fast_pad_link (srcpad, sinkpad) != GST_PAD_LINK_OK)) + goto passthrough_link_failure; + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + srcpad = NULL; + + /* Path 2 : Conversion / Encoding */ + + /* 1. Create the encoder */ + GST_LOG ("Adding encoder"); + sgroup->encoder = _get_encoder (ebin, sprof); + if (sgroup->encoder != NULL) { + gst_bin_add ((GstBin *) ebin, sgroup->encoder); + tosync = g_list_append (tosync, sgroup->encoder); + + sinkpad = + local_element_request_pad (sgroup->combiner, NULL, "encodingsink", + NULL); + if (G_UNLIKELY (sinkpad == NULL)) + goto no_combiner_sinkpad; + srcpad = gst_element_get_static_pad (sgroup->encoder, "src"); + if (G_UNLIKELY (fast_pad_link (srcpad, sinkpad) != GST_PAD_LINK_OK)) + goto encoder_link_failure; + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + srcpad = NULL; + } else if (gst_encoding_profile_get_preset (sgroup->profile) + || gst_encoding_profile_get_preset_name (sgroup->profile)) { + + if (!encoder_not_found) + _post_missing_plugin_message (ebin, sprof); + else + *encoder_not_found = TRUE; + goto cleanup; + } else { + /* passthrough can still work, if we discover that * + * encoding is required we post a missing plugin message */ + } + + + /* 3. Create the conversion/restriction elements */ + /* 3.1. capsfilter */ + GST_LOG ("Adding capsfilter for restriction caps : %" GST_PTR_FORMAT, + restriction); + + last = sgroup->capsfilter = gst_element_factory_make ("capsfilter", NULL); + if (restriction && !gst_caps_is_any (restriction)) + g_object_set (sgroup->capsfilter, "caps", restriction, NULL); + + if (!gst_encoding_profile_get_allow_dynamic_output (sprof)) { + if (!sgroup->inputfilter_caps_sid) { + sgroup->inputfilter_caps_sid = + g_signal_connect (sgroup->capsfilter->sinkpads->data, + "notify::caps", G_CALLBACK (_capsfilter_force_format), + &sgroup->inputfilter_caps_sid); + } + } + + gst_bin_add ((GstBin *) ebin, sgroup->capsfilter); + tosync = g_list_append (tosync, sgroup->capsfilter); + if (sgroup->encoder == NULL) { + /* no encoder available but it might be possible to just do passthrough, so + * let's just set up a fake pad to detect that encoding was attempted and + * if so it posts the missing plugin message */ + sgroup->fakesink = gst_element_factory_make ("fakesink", NULL); + g_object_set (sgroup->fakesink, "async", FALSE, NULL); + gst_bin_add (GST_BIN_CAST (ebin), sgroup->fakesink); + tosync = g_list_append (tosync, sgroup->fakesink); + encoder = sgroup->fakesink; + + _set_up_fake_encoder_pad_probe (ebin, sgroup); + } else { + encoder = sgroup->encoder; + } + fast_element_link (sgroup->capsfilter, encoder); + sgroup->restriction_sid = g_signal_connect (sprof, "notify::restriction-caps", + G_CALLBACK (_profile_restriction_caps_cb), sgroup); + + /* 3.2. restriction elements */ + /* FIXME : Once we have properties for specific converters, use those */ + if (GST_IS_ENCODING_VIDEO_PROFILE (sprof)) { + const gboolean native_video = + ! !(ebin->flags & GST_ENCODEBIN_FLAG_NO_VIDEO_CONVERSION); + GstElement *cspace = NULL, *scale, *vrate, *cspace2 = NULL; + + GST_LOG ("Adding conversion elements for video stream"); + + if (!native_video) { + cspace = gst_element_factory_make ("videoconvert", NULL); + scale = gst_element_factory_make ("videoscale", NULL); + if (!scale) { + missing_element_name = "videoscale"; + goto missing_element; + } + /* 4-tap scaling and black borders */ + g_object_set (scale, "method", 2, "add-borders", TRUE, NULL); + cspace2 = gst_element_factory_make ("videoconvert", NULL); + + if (!cspace || !cspace2) { + missing_element_name = "videoconvert"; + goto missing_element; + } + + gst_bin_add_many ((GstBin *) ebin, cspace, scale, cspace2, NULL); + tosync = g_list_append (tosync, cspace); + tosync = g_list_append (tosync, scale); + tosync = g_list_append (tosync, cspace2); + + sgroup->converters = g_list_prepend (sgroup->converters, cspace); + sgroup->converters = g_list_prepend (sgroup->converters, scale); + sgroup->converters = g_list_prepend (sgroup->converters, cspace2); + + if (!fast_element_link (cspace, scale) || + !fast_element_link (scale, cspace2)) + goto converter_link_failure; + } + + if (!gst_encoding_video_profile_get_variableframerate + (GST_ENCODING_VIDEO_PROFILE (sprof))) { + vrate = gst_element_factory_make ("videorate", NULL); + if (!vrate) { + missing_element_name = "videorate"; + goto missing_element; + } + g_object_set (vrate, "skip-to-first", TRUE, NULL); + + gst_bin_add ((GstBin *) ebin, vrate); + tosync = g_list_prepend (tosync, vrate); + sgroup->converters = g_list_prepend (sgroup->converters, vrate); + + if ((!native_video && !fast_element_link (cspace2, vrate)) + || !fast_element_link (vrate, last)) + goto converter_link_failure; + + if (!native_video) + last = cspace; + else + last = vrate; + } else if (!native_video) { + if (!fast_element_link (cspace2, last)) + goto converter_link_failure; + last = cspace; + } + + } else if (GST_IS_ENCODING_AUDIO_PROFILE (sprof) + && !(ebin->flags & GST_ENCODEBIN_FLAG_NO_AUDIO_CONVERSION)) { + GstElement *aconv, *ares, *arate, *aconv2; + + GST_LOG ("Adding conversion elements for audio stream"); + + arate = gst_element_factory_make ("audiorate", NULL); + if (!arate) { + missing_element_name = "audiorate"; + goto missing_element; + } + g_object_set (arate, "tolerance", (guint64) ebin->tolerance, NULL); + g_object_set (arate, "skip-to-first", TRUE, NULL); + + aconv = gst_element_factory_make ("audioconvert", NULL); + aconv2 = gst_element_factory_make ("audioconvert", NULL); + ares = gst_element_factory_make ("audioresample", NULL); + if (!aconv || !aconv2) { + missing_element_name = "audioconvert"; + goto missing_element; + } + if (!ares) { + missing_element_name = "audioresample"; + goto missing_element; + } + + gst_bin_add_many ((GstBin *) ebin, arate, aconv, ares, aconv2, NULL); + tosync = g_list_append (tosync, arate); + tosync = g_list_append (tosync, aconv); + tosync = g_list_append (tosync, ares); + tosync = g_list_append (tosync, aconv2); + if (!fast_element_link (arate, aconv) || + !fast_element_link (aconv, ares) || + !fast_element_link (ares, aconv2) || !fast_element_link (aconv2, last)) + goto converter_link_failure; + + sgroup->converters = g_list_prepend (sgroup->converters, arate); + sgroup->converters = g_list_prepend (sgroup->converters, aconv); + sgroup->converters = g_list_prepend (sgroup->converters, ares); + sgroup->converters = g_list_prepend (sgroup->converters, aconv2); + + last = arate; + } + + /* Link to stream splitter */ + sinkpad = gst_element_get_static_pad (last, "sink"); + srcpad = + local_element_request_pad (sgroup->splitter, NULL, "encodingsrc", NULL); + if (G_UNLIKELY (srcpad == NULL)) + goto no_splitter_srcpad; + if (G_UNLIKELY (fast_pad_link (srcpad, sinkpad) != GST_PAD_LINK_OK)) + goto splitter_encoding_failure; + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + srcpad = NULL; + + /* End of Stream 2 setup */ + + /* Sync all elements to parent state */ + for (tmp = tosync; tmp; tmp = tmp->next) + gst_element_sync_state_with_parent ((GstElement *) tmp->data); + g_list_free (tosync); + + /* Add ghostpad */ + GST_DEBUG ("Adding ghostpad %s:%s", GST_DEBUG_PAD_NAME (sgroup->ghostpad)); + gst_pad_set_active (sgroup->ghostpad, TRUE); + gst_element_add_pad ((GstElement *) ebin, sgroup->ghostpad); + + /* Add StreamGroup to our list of streams */ + + GST_DEBUG + ("Done creating elements, adding StreamGroup to our controlled stream list"); + + ebin->streams = g_list_prepend (ebin->streams, sgroup); + + if (format) + gst_caps_unref (format); + if (restriction) + gst_caps_unref (restriction); + + return sgroup; + +splitter_encoding_failure: + GST_ERROR_OBJECT (ebin, "Error linking splitter to encoding stream"); + goto cleanup; + +no_muxer_pad: + GST_ERROR_OBJECT (ebin, + "Couldn't find a compatible muxer pad to link encoder to"); + goto cleanup; + +missing_element: + gst_element_post_message (GST_ELEMENT_CAST (ebin), + gst_missing_element_message_new (GST_ELEMENT_CAST (ebin), + missing_element_name)); + GST_ELEMENT_ERROR (ebin, CORE, MISSING_PLUGIN, + (_("Missing element '%s' - check your GStreamer installation."), + missing_element_name), (NULL)); + goto cleanup; + +encoder_link_failure: + GST_ERROR_OBJECT (ebin, "Failed to link the encoder"); + goto cleanup; + +muxer_link_failure: + GST_ERROR_OBJECT (ebin, "Couldn't link encoder to muxer"); + goto cleanup; + +formatter_link_failure: + GST_ERROR_OBJECT (ebin, "Couldn't link output filter to output queue"); + goto cleanup; + +outfilter_link_failure: + GST_ERROR_OBJECT (ebin, + "Couldn't link output filter to output queue/formatter"); + goto cleanup; + +passthrough_link_failure: + GST_ERROR_OBJECT (ebin, "Failed linking splitter in passthrough mode"); + goto cleanup; + +no_splitter_srcpad: + GST_ERROR_OBJECT (ebin, "Couldn't get a source pad from the splitter"); + goto cleanup; + +no_combiner_sinkpad: + GST_ERROR_OBJECT (ebin, "Couldn't get a sink pad from the combiner"); + goto cleanup; + +splitter_link_failure: + GST_ERROR_OBJECT (ebin, "Failure linking to the splitter"); + goto cleanup; + +combiner_link_failure: + GST_ERROR_OBJECT (ebin, "Failure linking to the combiner"); + goto cleanup; + +parser_link_failure: + GST_ERROR_OBJECT (ebin, "Failure linking the parser"); + goto cleanup; + +converter_link_failure: + GST_ERROR_OBJECT (ebin, "Failure linking the video converters"); + goto cleanup; + +cleanup: + /* FIXME : Actually properly cleanup everything */ + if (format) + gst_caps_unref (format); + if (restriction) + gst_caps_unref (restriction); + if (srcpad) + gst_object_unref (srcpad); + stream_group_free (ebin, sgroup); + g_list_free (tosync); + return NULL; +} + +static gboolean +_gst_caps_match_foreach (GQuark field_id, const GValue * value, gpointer data) +{ + GstStructure *structure = data; + const GValue *other_value = gst_structure_id_get_value (structure, field_id); + + if (G_UNLIKELY (other_value == NULL)) + return FALSE; + if (gst_value_compare (value, other_value) == GST_VALUE_EQUAL) { + return TRUE; + } + + return FALSE; +} + +/* + * checks that there is at least one structure on caps_a that has + * all its fields exactly the same as one structure on caps_b + */ +static gboolean +_gst_caps_match (const GstCaps * caps_a, const GstCaps * caps_b) +{ + gint i, j; + gboolean res = FALSE; + + for (i = 0; i < gst_caps_get_size (caps_a); i++) { + GstStructure *structure_a = gst_caps_get_structure (caps_a, i); + for (j = 0; j < gst_caps_get_size (caps_b); j++) { + GstStructure *structure_b = gst_caps_get_structure (caps_b, j); + + res = gst_structure_foreach (structure_a, _gst_caps_match_foreach, + structure_b); + if (res) + goto end; + } + } +end: + return res; +} + +static gboolean +_factory_can_handle_caps (GstElementFactory * factory, const GstCaps * caps, + GstPadDirection dir, gboolean exact) +{ + const GList *templates; + + templates = gst_element_factory_get_static_pad_templates (factory); + while (templates) { + GstStaticPadTemplate *template = (GstStaticPadTemplate *) templates->data; + + if (template->direction == dir) { + GstCaps *tmp = gst_static_caps_get (&template->static_caps); + + if ((exact && _gst_caps_match (caps, tmp)) || + (!exact && gst_caps_can_intersect (tmp, caps))) { + gst_caps_unref (tmp); + return TRUE; + } + gst_caps_unref (tmp); + } + templates = g_list_next (templates); + } + + return FALSE; +} + +static inline GstElement * +_get_formatter (GstEncodeBin * ebin, GstEncodingProfile * sprof) +{ + GList *formatters, *tmpfmtr; + GstElement *formatter = NULL; + GstElementFactory *formatterfact = NULL; + GstCaps *format; + const gchar *preset, *preset_name; + + format = gst_encoding_profile_get_format (sprof); + preset = gst_encoding_profile_get_preset (sprof); + preset_name = gst_encoding_profile_get_preset_name (sprof); + + GST_DEBUG ("Getting list of formatters for format %" GST_PTR_FORMAT, format); + + formatters = + gst_element_factory_list_filter (ebin->formatters, format, GST_PAD_SRC, + FALSE); + + if (formatters == NULL) + goto beach; + + /* FIXME : signal the user if he wants this */ + for (tmpfmtr = formatters; tmpfmtr; tmpfmtr = tmpfmtr->next) { + formatterfact = (GstElementFactory *) tmpfmtr->data; + + GST_DEBUG_OBJECT (ebin, "Trying formatter %s", + GST_OBJECT_NAME (formatterfact)); + + if ((formatter = + _create_element_and_set_preset (formatterfact, preset, + NULL, preset_name))) + break; + } + + gst_plugin_feature_list_free (formatters); + +beach: + if (format) + gst_caps_unref (format); + return formatter; +} + +static gint +compare_elements (gconstpointer a, gconstpointer b, gpointer udata) +{ + GstCaps *caps = udata; + GstElementFactory *fac_a = (GstElementFactory *) a; + GstElementFactory *fac_b = (GstElementFactory *) b; + + /* FIXME not quite sure this is the best algorithm to order the elements + * Some caps similarity comparison algorithm would fit better than going + * boolean (equals/not equals). + */ + gboolean equals_a = _factory_can_handle_caps (fac_a, caps, GST_PAD_SRC, TRUE); + gboolean equals_b = _factory_can_handle_caps (fac_b, caps, GST_PAD_SRC, TRUE); + + if (equals_a == equals_b) { + return gst_plugin_feature_get_rank ((GstPluginFeature *) fac_b) - + gst_plugin_feature_get_rank ((GstPluginFeature *) fac_a); + } else if (equals_a) { + return -1; + } else if (equals_b) { + return 1; + } + return 0; +} + +static inline GstElement * +_get_muxer (GstEncodeBin * ebin) +{ + GList *muxers, *formatters, *tmpmux; + GstElement *muxer = NULL; + GstElementFactory *muxerfact = NULL; + const GList *tmp; + GstCaps *format; + const gchar *preset, *preset_name; + + format = gst_encoding_profile_get_format (ebin->profile); + preset = gst_encoding_profile_get_preset (ebin->profile); + preset_name = gst_encoding_profile_get_preset_name (ebin->profile); + + GST_DEBUG ("Getting list of muxers for format %" GST_PTR_FORMAT, format); + + muxers = + gst_element_factory_list_filter (ebin->muxers, format, GST_PAD_SRC, TRUE); + + formatters = + gst_element_factory_list_filter (ebin->formatters, format, GST_PAD_SRC, + TRUE); + + muxers = g_list_sort_with_data (muxers, compare_elements, (gpointer) format); + formatters = + g_list_sort_with_data (formatters, compare_elements, (gpointer) format); + + muxers = g_list_concat (muxers, formatters); + + if (muxers == NULL) + goto beach; + + /* FIXME : signal the user if he wants this */ + for (tmpmux = muxers; tmpmux; tmpmux = tmpmux->next) { + gboolean cansinkstreams = TRUE; + const GList *profiles = + gst_encoding_container_profile_get_profiles + (GST_ENCODING_CONTAINER_PROFILE (ebin->profile)); + + muxerfact = (GstElementFactory *) tmpmux->data; + + GST_DEBUG ("Trying muxer %s", GST_OBJECT_NAME (muxerfact)); + + /* See if the muxer can sink all of our stream profile caps */ + for (tmp = profiles; tmp; tmp = tmp->next) { + GstEncodingProfile *sprof = (GstEncodingProfile *) tmp->data; + GstCaps *sformat = gst_encoding_profile_get_format (sprof); + + if (!_factory_can_handle_caps (muxerfact, sformat, GST_PAD_SINK, FALSE)) { + GST_DEBUG ("Skipping muxer because it can't sink caps %" + GST_PTR_FORMAT, sformat); + cansinkstreams = FALSE; + if (sformat) + gst_caps_unref (sformat); + break; + } + if (sformat) + gst_caps_unref (sformat); + } + + /* Only use a muxer than can use all streams and than can accept the + * preset (which may be present or not) */ + if (cansinkstreams && (muxer = + _create_element_and_set_preset (muxerfact, preset, "muxer", + preset_name))) + break; + } + + gst_plugin_feature_list_free (muxers); + +beach: + if (format) + gst_caps_unref (format); + return muxer; +} + +static gboolean +create_elements_and_pads (GstEncodeBin * ebin) +{ + gboolean ret = TRUE; + GstElement *muxer = NULL; + GstPad *muxerpad; + const GList *tmp, *profiles; + GstEncodingProfile *sprof; + + GST_DEBUG ("Current profile : %s", + gst_encoding_profile_get_name (ebin->profile)); + + if (GST_IS_ENCODING_CONTAINER_PROFILE (ebin->profile)) { + /* 1. Get the compatible muxer */ + muxer = _get_muxer (ebin); + if (G_UNLIKELY (muxer == NULL)) + goto no_muxer; + + /* Record the muxer */ + ebin->muxer = muxer; + gst_bin_add ((GstBin *) ebin, muxer); + + /* 2. Ghost the muxer source pad */ + + /* FIXME : We should figure out if it's a static/request/dyamic pad, + * but for the time being let's assume it's a static pad :) */ + muxerpad = gst_element_get_static_pad (muxer, "src"); + if (G_UNLIKELY (muxerpad == NULL)) + goto no_muxer_pad; + + if (!gst_ghost_pad_set_target (GST_GHOST_PAD (ebin->srcpad), muxerpad)) + goto no_muxer_ghost_pad; + + gst_object_unref (muxerpad); + /* 3. Activate fixed presence streams */ + profiles = + gst_encoding_container_profile_get_profiles + (GST_ENCODING_CONTAINER_PROFILE (ebin->profile)); + for (tmp = profiles; tmp; tmp = tmp->next) { + sprof = (GstEncodingProfile *) tmp->data; + + GST_DEBUG ("Trying stream profile with presence %d", + gst_encoding_profile_get_presence (sprof)); + + if (gst_encoding_profile_get_presence (sprof) != 0 && + gst_encoding_profile_is_enabled (sprof)) { + if (G_UNLIKELY (_create_stream_group (ebin, sprof, NULL, NULL, + NULL) == NULL)) + goto stream_error; + } + } + gst_element_sync_state_with_parent (muxer); + } else { + if (G_UNLIKELY (_create_stream_group (ebin, ebin->profile, NULL, + NULL, NULL) == NULL)) + goto stream_error; + } + + return ret; + +no_muxer: + { + GstCaps *format = gst_encoding_profile_get_format (ebin->profile); + + GST_WARNING ("No available muxer for %" GST_PTR_FORMAT, format); + /* missing plugin support */ + gst_element_post_message (GST_ELEMENT_CAST (ebin), + gst_missing_encoder_message_new (GST_ELEMENT_CAST (ebin), format)); + GST_ELEMENT_ERROR (ebin, CORE, MISSING_PLUGIN, + ("No available muxer for format %" GST_PTR_FORMAT, format), (NULL)); + if (format) + gst_caps_unref (format); + return FALSE; + } + +no_muxer_pad: + { + GST_WARNING ("Can't get source pad from muxer (%s)", + GST_ELEMENT_NAME (muxer)); + gst_bin_remove (GST_BIN (ebin), muxer); + return FALSE; + } + +no_muxer_ghost_pad: + { + GST_WARNING ("Couldn't set %s:%s as source ghostpad target", + GST_DEBUG_PAD_NAME (muxerpad)); + gst_bin_remove (GST_BIN (ebin), muxer); + gst_object_unref (muxerpad); + return FALSE; + } + +stream_error: + { + GST_WARNING ("Could not create Streams"); + if (muxer) + gst_bin_remove (GST_BIN (ebin), muxer); + ebin->muxer = NULL; + return FALSE; + } +} + +static void +release_pads (const GValue * item, GstElement * elt) +{ + GstPad *pad = g_value_get_object (item); + GstPad *peer = NULL; + + GST_DEBUG_OBJECT (elt, "Releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + + /* Unlink from its peer pad */ + if ((peer = gst_pad_get_peer (pad))) { + if (GST_PAD_DIRECTION (peer) == GST_PAD_SRC) + gst_pad_unlink (peer, pad); + else + gst_pad_unlink (pad, peer); + gst_object_unref (peer); + } + + /* Release it from the object */ + gst_element_release_request_pad (elt, pad); +} + +static void +stream_group_free (GstEncodeBin * ebin, StreamGroup * sgroup) +{ + GList *tmp; + GstPad *tmppad; + GstPad *pad; + + GST_DEBUG_OBJECT (ebin, "Freeing StreamGroup %p", sgroup); + + if (sgroup->restriction_sid != 0) + g_signal_handler_disconnect (sgroup->profile, sgroup->restriction_sid); + + if (sgroup->outqueue) { + if (ebin->muxer) { + /* outqueue - Muxer */ + tmppad = gst_element_get_static_pad (sgroup->outqueue, "src"); + pad = gst_pad_get_peer (tmppad); + + if (pad) { + /* Remove muxer request sink pad */ + gst_pad_unlink (tmppad, pad); + if (GST_PAD_TEMPLATE_PRESENCE (GST_PAD_PAD_TEMPLATE (pad)) == + GST_PAD_REQUEST) + gst_element_release_request_pad (ebin->muxer, pad); + gst_object_unref (pad); + } + gst_object_unref (tmppad); + } + gst_element_set_state (sgroup->outqueue, GST_STATE_NULL); + } + + if (sgroup->formatter) { + /* capsfilter - formatter - outqueue */ + gst_element_set_state (sgroup->formatter, GST_STATE_NULL); + gst_element_set_state (sgroup->outfilter, GST_STATE_NULL); + gst_element_unlink (sgroup->formatter, sgroup->outqueue); + gst_element_unlink (sgroup->outfilter, sgroup->formatter); + } else if (sgroup->outfilter) { + /* Capsfilter - outqueue */ + gst_element_set_state (sgroup->outfilter, GST_STATE_NULL); + gst_element_unlink (sgroup->outfilter, sgroup->outqueue); + } + + if (sgroup->outqueue) { + gst_element_set_state (sgroup->outqueue, GST_STATE_NULL); + gst_bin_remove (GST_BIN (ebin), sgroup->outqueue); + } + + /* streamcombiner - parser - capsfilter */ + if (sgroup->parser) { + gst_element_set_state (sgroup->parser, GST_STATE_NULL); + gst_element_unlink (sgroup->parser, sgroup->outfilter); + gst_element_unlink (sgroup->combiner, sgroup->parser); + gst_bin_remove ((GstBin *) ebin, sgroup->parser); + } + + /* Sink Ghostpad */ + if (sgroup->ghostpad) { + if (GST_PAD_PARENT (sgroup->ghostpad) != NULL) + gst_element_remove_pad (GST_ELEMENT_CAST (ebin), sgroup->ghostpad); + else + gst_object_unref (sgroup->ghostpad); + } + + if (sgroup->inqueue) + gst_element_set_state (sgroup->inqueue, GST_STATE_NULL); + + if (sgroup->encoder) + gst_element_set_state (sgroup->encoder, GST_STATE_NULL); + if (sgroup->fakesink) + gst_element_set_state (sgroup->fakesink, GST_STATE_NULL); + if (sgroup->outfilter) { + gst_element_set_state (sgroup->outfilter, GST_STATE_NULL); + + if (sgroup->outputfilter_caps_sid) { + g_signal_handler_disconnect (sgroup->outfilter->sinkpads->data, + sgroup->outputfilter_caps_sid); + sgroup->outputfilter_caps_sid = 0; + } + } + if (sgroup->smartencoder) + gst_element_set_state (sgroup->smartencoder, GST_STATE_NULL); + + if (sgroup->capsfilter) { + gst_element_set_state (sgroup->capsfilter, GST_STATE_NULL); + if (sgroup->encoder) + gst_element_unlink (sgroup->capsfilter, sgroup->encoder); + else + gst_element_unlink (sgroup->capsfilter, sgroup->fakesink); + + if (sgroup->inputfilter_caps_sid) { + g_signal_handler_disconnect (sgroup->capsfilter->sinkpads->data, + sgroup->inputfilter_caps_sid); + sgroup->inputfilter_caps_sid = 0; + } + gst_bin_remove ((GstBin *) ebin, sgroup->capsfilter); + } + + for (tmp = sgroup->converters; tmp; tmp = tmp->next) { + GstElement *elt = (GstElement *) tmp->data; + + gst_element_set_state (elt, GST_STATE_NULL); + gst_bin_remove ((GstBin *) ebin, elt); + } + if (sgroup->converters) + g_list_free (sgroup->converters); + + if (sgroup->combiner) { + GstIterator *it = gst_element_iterate_sink_pads (sgroup->combiner); + GstIteratorResult itret = GST_ITERATOR_OK; + + while (itret == GST_ITERATOR_OK || itret == GST_ITERATOR_RESYNC) { + itret = + gst_iterator_foreach (it, (GstIteratorForeachFunction) release_pads, + sgroup->combiner); + gst_iterator_resync (it); + } + gst_iterator_free (it); + gst_element_set_state (sgroup->combiner, GST_STATE_NULL); + gst_bin_remove ((GstBin *) ebin, sgroup->combiner); + } + + if (sgroup->splitter) { + GstIterator *it = gst_element_iterate_src_pads (sgroup->splitter); + GstIteratorResult itret = GST_ITERATOR_OK; + while (itret == GST_ITERATOR_OK || itret == GST_ITERATOR_RESYNC) { + itret = + gst_iterator_foreach (it, (GstIteratorForeachFunction) release_pads, + sgroup->splitter); + gst_iterator_resync (it); + } + gst_iterator_free (it); + + gst_element_set_state (sgroup->splitter, GST_STATE_NULL); + gst_bin_remove ((GstBin *) ebin, sgroup->splitter); + } + + if (sgroup->inqueue) + gst_bin_remove ((GstBin *) ebin, sgroup->inqueue); + + if (sgroup->encoder) + gst_bin_remove ((GstBin *) ebin, sgroup->encoder); + + if (sgroup->fakesink) + gst_bin_remove ((GstBin *) ebin, sgroup->fakesink); + + if (sgroup->smartencoder) + gst_bin_remove ((GstBin *) ebin, sgroup->smartencoder); + + if (sgroup->outfilter) + gst_bin_remove ((GstBin *) ebin, sgroup->outfilter); + + g_slice_free (StreamGroup, sgroup); +} + +static void +stream_group_remove (GstEncodeBin * ebin, StreamGroup * sgroup) +{ + ebin->streams = g_list_remove (ebin->streams, sgroup); + + stream_group_free (ebin, sgroup); +} + +static void +gst_encode_bin_tear_down_profile (GstEncodeBin * ebin) +{ + if (G_UNLIKELY (ebin->profile == NULL)) + return; + + GST_DEBUG ("Tearing down profile %s", + gst_encoding_profile_get_name (ebin->profile)); + + while (ebin->streams) + stream_group_remove (ebin, (StreamGroup *) ebin->streams->data); + + /* Set ghostpad target to NULL */ + gst_ghost_pad_set_target (GST_GHOST_PAD (ebin->srcpad), NULL); + + /* Remove muxer if present */ + if (ebin->muxer) { + gst_element_set_state (ebin->muxer, GST_STATE_NULL); + gst_bin_remove (GST_BIN (ebin), ebin->muxer); + ebin->muxer = NULL; + } + + /* free/clear profile */ + gst_encoding_profile_unref (ebin->profile); + ebin->profile = NULL; +} + +static gboolean +gst_encode_bin_setup_profile (GstEncodeBin * ebin, GstEncodingProfile * profile) +{ + gboolean res; + + g_return_val_if_fail (ebin->profile == NULL, FALSE); + + GST_DEBUG ("Setting up profile %p:%s (type:%s)", profile, + gst_encoding_profile_get_name (profile), + gst_encoding_profile_get_type_nick (profile)); + + ebin->profile = profile; + gst_object_ref (ebin->profile); + + /* Create elements */ + res = create_elements_and_pads (ebin); + if (!res) + gst_encode_bin_tear_down_profile (ebin); + + return res; +} + +static gboolean +gst_encode_bin_set_profile (GstEncodeBin * ebin, GstEncodingProfile * profile) +{ + g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); + + GST_DEBUG_OBJECT (ebin, "profile (%p) : %s", profile, + gst_encoding_profile_get_name (profile)); + + if (G_UNLIKELY (ebin->active)) { + GST_WARNING_OBJECT (ebin, "Element already active, can't change profile"); + return FALSE; + } + + /* If we're not active, we can deactivate the previous profile */ + if (ebin->profile) { + gst_encode_bin_tear_down_profile (ebin); + } + + return gst_encode_bin_setup_profile (ebin, profile); +} + +static inline gboolean +gst_encode_bin_activate (GstEncodeBin * ebin) +{ + ebin->active = ebin->profile != NULL; + return ebin->active; +} + +static void +gst_encode_bin_deactivate (GstEncodeBin * ebin) +{ + GList *tmp; + + for (tmp = ebin->streams; tmp; tmp = tmp->next) { + StreamGroup *sgroup = tmp->data; + GstCaps *format = gst_encoding_profile_get_format (sgroup->profile); + + _set_group_caps_format (sgroup, sgroup->profile, format); + + if (format) + gst_caps_unref (format); + } + + ebin->active = FALSE; +} + +static GstStateChangeReturn +gst_encode_bin_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret; + GstEncodeBin *ebin = (GstEncodeBin *) element; + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + if (!gst_encode_bin_activate (ebin)) { + ret = GST_STATE_CHANGE_FAILURE; + goto beach; + } + break; + default: + break; + } + + ret = + GST_ELEMENT_CLASS (gst_encode_bin_parent_class)->change_state (element, + transition); + if (ret == GST_STATE_CHANGE_FAILURE) + goto beach; + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_encode_bin_deactivate (ebin); + break; + default: + break; + } + +beach: + return ret; +} + + +static gboolean +plugin_init (GstPlugin * plugin) +{ + gboolean res; + + GST_DEBUG_CATEGORY_INIT (gst_encode_bin_debug, "encodebin", 0, "encoder bin"); + +#ifdef ENABLE_NLS + GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE, + LOCALEDIR); + bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); +#endif /* ENABLE_NLS */ + + + res = gst_element_register (plugin, "encodebin", GST_RANK_NONE, + GST_TYPE_ENCODE_BIN); + + return res; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + encoding, + "various encoding-related elements", plugin_init, VERSION, GST_LICENSE, + GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/gst/encoding/gstencodebasebin.h b/gst/encoding/gstencodebasebin.h new file mode 100644 index 0000000..021e690 --- /dev/null +++ b/gst/encoding/gstencodebasebin.h @@ -0,0 +1,38 @@ +/* GStreamer encoding bin + * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk> + * (C) 2009 Nokia Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_ENCODEBIN_H__ +#define __GST_ENCODEBIN_H__ + +#include <gst/gst.h> +#include <gst/pbutils/pbutils.h> + +#define GST_TYPE_ENCODE_BIN (gst_encode_bin_get_type()) +#define GST_ENCODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ENCODE_BIN,GstEncodeBin)) +#define GST_ENCODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ENCODE_BIN,GstEncodeBinClass)) +#define GST_IS_ENCODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ENCODE_BIN)) +#define GST_IS_ENCODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ENCODE_BIN)) + +typedef struct _GstEncodeBin GstEncodeBin; +typedef struct _GstEncodeBinClass GstEncodeBinClass; + +GType gst_encode_bin_get_type(void); + +#endif /* __GST_ENCODEBIN_H__ */ diff --git a/gst/encoding/gstencodebin.c b/gst/encoding/gstencodebin.c deleted file mode 100644 index 6f8cc5e..0000000 --- a/gst/encoding/gstencodebin.c +++ /dev/null @@ -1,2351 +0,0 @@ -/* GStreamer encoding bin - * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk> - * (C) 2009 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <string.h> -#include "gstencodebin.h" -#include "gstsmartencoder.h" -#include "gststreamsplitter.h" -#include "gststreamcombiner.h" -#include <gst/gst-i18n-plugin.h> - -/** - * SECTION:element-encodebin - * @title: encodebin - * - * EncodeBin provides a bin for encoding/muxing various streams according to - * a specified #GstEncodingProfile. - * - * Based on the profile that was set (via the #GstEncodeBin:profile property), - * EncodeBin will internally select and configure the required elements - * (encoders, muxers, but also audio and video converters) so that you can - * provide it raw or pre-encoded streams of data in input and have your - * encoded/muxed/converted stream in output. - * - * ## Features - * - * * Automatic encoder and muxer selection based on elements available on the - * system. - * - * * Conversion of raw audio/video streams (scaling, framerate conversion, - * colorspace conversion, samplerate conversion) to conform to the profile - * output format. - * - * * Variable number of streams. If the presence property for a stream encoding - * profile is 0, you can request any number of sink pads for it via the - * standard request pad gstreamer API or the #GstEncodeBin::request-pad action - * signal. - * - * * Avoid reencoding (passthrough). If the input stream is already encoded and is - * compatible with what the #GstEncodingProfile expects, then the stream won't - * be re-encoded but just passed through downstream to the muxer or the output. - * - * * Mix pre-encoded and raw streams as input. In addition to the passthrough - * feature above, you can feed both raw audio/video *AND* already-encoded data - * to a pad. #GstEncodeBin will take care of passing through the compatible - * segments and re-encoding the segments of media that need encoding. - * - * * Standard behaviour is to use a #GstEncodingContainerProfile to have both - * encoding and muxing performed. But you can also provide a single stream - * profile (like #GstEncodingAudioProfile) to only have the encoding done and - * handle the encoded output yourself. - * - * * Audio imperfection corrections. Incoming audio streams can have non perfect - * timestamps (jitter), like the streams coming from ASF files. #GstEncodeBin - * will automatically fix those imperfections for you. See - * #GstEncodeBin:audio-jitter-tolerance for more details. - * - * * Variable or Constant video framerate. If your #GstEncodingVideoProfile has - * the variableframerate property deactivated (default), then the incoming - * raw video stream will be retimestampped in order to produce a constant - * framerate. - * - * * Cross-boundary re-encoding. When feeding compatible pre-encoded streams that - * fall on segment boundaries, and for supported formats (right now only H263), - * the GOP will be decoded/reencoded when needed to produce an encoded output - * that fits exactly within the request GstSegment. - * - * * Missing plugin support. If a #GstElement is missing to encode/mux to the - * request profile formats, a missing-plugin #GstMessage will be posted on the - * #GstBus, allowing systems that support the missing-plugin system to offer the - * user a way to install the missing element. - * - */ - - -/* TODO/FIXME - * - * Handling mp3!xing!idv3 and theora!ogg tagsetting scenarios: - * Once we have chosen a muxer: - * When a new stream is requested: - * If muxer isn't 'Formatter' OR doesn't have a TagSetter interface: - * Find a Formatter for the given stream (preferably with TagSetter) - * Insert that before muxer - **/ - -#define fast_pad_link(a,b) gst_pad_link_full((a),(b),GST_PAD_LINK_CHECK_NOTHING) -#define fast_element_link(a,b) gst_element_link_pads_full((a),"src",(b),"sink",GST_PAD_LINK_CHECK_NOTHING) - -typedef enum -{ - GST_ENCODEBIN_FLAG_NO_AUDIO_CONVERSION = (1 << 0), - GST_ENCODEBIN_FLAG_NO_VIDEO_CONVERSION = (1 << 1) -} GstEncodeBinFlags; - -#define GST_TYPE_ENCODEBIN_FLAGS (gst_encodebin_flags_get_type()) -GType gst_encodebin_flags_get_type (void); - -/* generic templates */ -static GstStaticPadTemplate muxer_src_template = -GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); - -static GstStaticPadTemplate video_sink_template = -GST_STATIC_PAD_TEMPLATE ("video_%u", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS_ANY); -static GstStaticPadTemplate audio_sink_template = -GST_STATIC_PAD_TEMPLATE ("audio_%u", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS_ANY); -/* static GstStaticPadTemplate text_sink_template = */ -/* GST_STATIC_PAD_TEMPLATE ("text_%u", */ -/* GST_PAD_SINK, */ -/* GST_PAD_REQUEST, */ -/* GST_STATIC_CAPS_ANY); */ -static GstStaticPadTemplate private_sink_template = -GST_STATIC_PAD_TEMPLATE ("private_%u", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS_ANY); - -struct _GstEncodeBin -{ - GstBin parent; - - /* the profile field is only valid if it could be entirely setup */ - GstEncodingProfile *profile; - - GList *streams; /* List of StreamGroup, not sorted */ - - GstElement *muxer; - /* Ghostpad with changing target */ - GstPad *srcpad; - - /* TRUE if in PAUSED/PLAYING */ - gboolean active; - - /* available muxers, encoders and parsers */ - GList *muxers; - GList *formatters; - GList *encoders; - GList *parsers; - - /* Increasing counter for unique pad name */ - guint last_pad_id; - - /* Cached caps for identification */ - GstCaps *raw_video_caps; - GstCaps *raw_audio_caps; - /* GstCaps *raw_text_caps; */ - - guint queue_buffers_max; - guint queue_bytes_max; - guint64 queue_time_max; - - guint64 tolerance; - gboolean avoid_reencoding; - - GstEncodeBinFlags flags; -}; - -struct _GstEncodeBinClass -{ - GstBinClass parent; - - /* Action Signals */ - GstPad *(*request_pad) (GstEncodeBin * encodebin, GstCaps * caps); - GstPad *(*request_profile_pad) (GstEncodeBin * encodebin, - const gchar * profilename); -}; - -typedef struct _StreamGroup StreamGroup; - -struct _StreamGroup -{ - GstEncodeBin *ebin; - GstEncodingProfile *profile; - GstPad *ghostpad; /* Sink ghostpad */ - GstElement *inqueue; /* Queue just after the ghostpad */ - GstElement *splitter; - GList *converters; /* List of conversion GstElement */ - GstElement *capsfilter; /* profile->restriction (if non-NULL/ANY) */ - gulong inputfilter_caps_sid; - GstElement *encoder; /* Encoder (can be NULL) */ - GstElement *fakesink; /* Fakesink (can be NULL) */ - GstElement *combiner; - GstElement *parser; - GstElement *smartencoder; - GstElement *outfilter; /* Output capsfilter (streamprofile.format) */ - gulong outputfilter_caps_sid; - GstElement *formatter; - GstElement *outqueue; /* Queue just before the muxer */ - gulong restriction_sid; -}; - -/* Default for queues (same defaults as queue element) */ -#define DEFAULT_QUEUE_BUFFERS_MAX 200 -#define DEFAULT_QUEUE_BYTES_MAX 10 * 1024 * 1024 -#define DEFAULT_QUEUE_TIME_MAX GST_SECOND -#define DEFAULT_AUDIO_JITTER_TOLERANCE 20 * GST_MSECOND -#define DEFAULT_AVOID_REENCODING FALSE -#define DEFAULT_FLAGS 0 - -#define DEFAULT_RAW_CAPS \ - "video/x-raw; " \ - "audio/x-raw; " \ - "text/x-raw; " \ - "subpicture/x-dvd; " \ - "subpicture/x-pgs" - -/* Properties */ -enum -{ - PROP_0, - PROP_PROFILE, - PROP_QUEUE_BUFFERS_MAX, - PROP_QUEUE_BYTES_MAX, - PROP_QUEUE_TIME_MAX, - PROP_AUDIO_JITTER_TOLERANCE, - PROP_AVOID_REENCODING, - PROP_FLAGS -}; - -/* Signals */ -enum -{ - SIGNAL_REQUEST_PAD, - SIGNAL_REQUEST_PROFILE_PAD, - LAST_SIGNAL -}; - -#define C_FLAGS(v) ((guint) v) - -GType -gst_encodebin_flags_get_type (void) -{ - static const GFlagsValue values[] = { - {C_FLAGS (GST_ENCODEBIN_FLAG_NO_AUDIO_CONVERSION), "Do not use audio " - "conversion elements", "no-audio-conversion"}, - {C_FLAGS (GST_ENCODEBIN_FLAG_NO_VIDEO_CONVERSION), "Do not use video " - "conversion elements", "no-video-conversion"}, - {0, NULL, NULL} - }; - static volatile GType id = 0; - - if (g_once_init_enter ((gsize *) & id)) { - GType _id; - - _id = g_flags_register_static ("GstEncodeBinFlags", values); - - g_once_init_leave ((gsize *) & id, _id); - } - - return id; -} - -static guint gst_encode_bin_signals[LAST_SIGNAL] = { 0 }; - -static GstStaticCaps default_raw_caps = GST_STATIC_CAPS (DEFAULT_RAW_CAPS); - -GST_DEBUG_CATEGORY_STATIC (gst_encode_bin_debug); -#define GST_CAT_DEFAULT gst_encode_bin_debug - -G_DEFINE_TYPE (GstEncodeBin, gst_encode_bin, GST_TYPE_BIN); - -static void gst_encode_bin_dispose (GObject * object); -static void gst_encode_bin_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_encode_bin_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static GstStateChangeReturn gst_encode_bin_change_state (GstElement * element, - GstStateChange transition); - -static GstPad *gst_encode_bin_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * name, const GstCaps * caps); -static void gst_encode_bin_release_pad (GstElement * element, GstPad * pad); - -static gboolean -gst_encode_bin_set_profile (GstEncodeBin * ebin, GstEncodingProfile * profile); -static void gst_encode_bin_tear_down_profile (GstEncodeBin * ebin); -static gboolean gst_encode_bin_setup_profile (GstEncodeBin * ebin, - GstEncodingProfile * profile); - -static StreamGroup *_create_stream_group (GstEncodeBin * ebin, - GstEncodingProfile * sprof, const gchar * sinkpadname, GstCaps * sinkcaps, - gboolean * encoder_not_found); -static void stream_group_remove (GstEncodeBin * ebin, StreamGroup * sgroup); -static void stream_group_free (GstEncodeBin * ebin, StreamGroup * sgroup); -static GstPad *gst_encode_bin_request_pad_signal (GstEncodeBin * encodebin, - GstCaps * caps); -static GstPad *gst_encode_bin_request_profile_pad_signal (GstEncodeBin * - encodebin, const gchar * profilename); - -static inline GstElement *_get_formatter (GstEncodeBin * ebin, - GstEncodingProfile * sprof); -static void _post_missing_plugin_message (GstEncodeBin * ebin, - GstEncodingProfile * prof); - -static void -gst_encode_bin_class_init (GstEncodeBinClass * klass) -{ - GObjectClass *gobject_klass; - GstElementClass *gstelement_klass; - - gobject_klass = (GObjectClass *) klass; - gstelement_klass = (GstElementClass *) klass; - - gobject_klass->dispose = gst_encode_bin_dispose; - gobject_klass->set_property = gst_encode_bin_set_property; - gobject_klass->get_property = gst_encode_bin_get_property; - - /* Properties */ - - /** - * GstEncodeBin:profile: - * - * The #GstEncodingProfile to use. This property must be set before going - * to %GST_STATE_PAUSED or higher. - */ - g_object_class_install_property (gobject_klass, PROP_PROFILE, - g_param_spec_object ("profile", "Profile", - "The GstEncodingProfile to use", GST_TYPE_ENCODING_PROFILE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_klass, PROP_QUEUE_BYTES_MAX, - g_param_spec_uint ("queue-bytes-max", "Max. size (kB)", - "Max. amount of data in the queue (bytes, 0=disable)", - 0, G_MAXUINT, DEFAULT_QUEUE_BYTES_MAX, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_klass, PROP_QUEUE_BUFFERS_MAX, - g_param_spec_uint ("queue-buffers-max", "Max. size (buffers)", - "Max. number of buffers in the queue (0=disable)", 0, G_MAXUINT, - DEFAULT_QUEUE_BUFFERS_MAX, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_klass, PROP_QUEUE_TIME_MAX, - g_param_spec_uint64 ("queue-time-max", "Max. size (ns)", - "Max. amount of data in the queue (in ns, 0=disable)", 0, G_MAXUINT64, - DEFAULT_QUEUE_TIME_MAX, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_klass, PROP_AUDIO_JITTER_TOLERANCE, - g_param_spec_uint64 ("audio-jitter-tolerance", "Audio jitter tolerance", - "Amount of timestamp jitter/imperfection to allow on audio streams before inserting/dropping samples (ns)", - 0, G_MAXUINT64, DEFAULT_AUDIO_JITTER_TOLERANCE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_klass, PROP_AVOID_REENCODING, - g_param_spec_boolean ("avoid-reencoding", "Avoid re-encoding", - "Whether to re-encode portions of compatible video streams that lay on segment boundaries", - DEFAULT_AVOID_REENCODING, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - /** - * GstEncodeBin:flags - * - * Control the behaviour of encodebin. - */ - g_object_class_install_property (gobject_klass, PROP_FLAGS, - g_param_spec_flags ("flags", "Flags", "Flags to control behaviour", - GST_TYPE_ENCODEBIN_FLAGS, DEFAULT_FLAGS, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - /* Signals */ - /** - * GstEncodeBin::request-pad - * @encodebin: a #GstEncodeBin instance - * @caps: a #GstCaps - * - * Use this method to request an unused sink request #GstPad that can take the - * provided @caps as input. You must release the pad with - * gst_element_release_request_pad() when you are done with it. - * - * Returns: A compatible #GstPad, or %NULL if no compatible #GstPad could be - * created or is available. - */ - gst_encode_bin_signals[SIGNAL_REQUEST_PAD] = - g_signal_new ("request-pad", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstEncodeBinClass, - request_pad), NULL, NULL, g_cclosure_marshal_generic, - GST_TYPE_PAD, 1, GST_TYPE_CAPS); - - /** - * GstEncodeBin::request-profile-pad - * @encodebin: a #GstEncodeBin instance - * @profilename: the name of a #GstEncodingProfile - * - * Use this method to request an unused sink request #GstPad from the profile - * @profilename. You must release the pad with - * gst_element_release_request_pad() when you are done with it. - * - * Returns: A compatible #GstPad, or %NULL if no compatible #GstPad could be - * created or is available. - */ - gst_encode_bin_signals[SIGNAL_REQUEST_PROFILE_PAD] = - g_signal_new ("request-profile-pad", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstEncodeBinClass, - request_profile_pad), NULL, NULL, g_cclosure_marshal_generic, - GST_TYPE_PAD, 1, G_TYPE_STRING); - - klass->request_pad = gst_encode_bin_request_pad_signal; - klass->request_profile_pad = gst_encode_bin_request_profile_pad_signal; - - gst_element_class_add_static_pad_template (gstelement_klass, - &muxer_src_template); - gst_element_class_add_static_pad_template (gstelement_klass, - &video_sink_template); - gst_element_class_add_static_pad_template (gstelement_klass, - &audio_sink_template); - /* gst_element_class_add_static_pad_template (gstelement_klass, &text_sink_template); */ - gst_element_class_add_static_pad_template (gstelement_klass, - &private_sink_template); - - gstelement_klass->change_state = - GST_DEBUG_FUNCPTR (gst_encode_bin_change_state); - gstelement_klass->request_new_pad = - GST_DEBUG_FUNCPTR (gst_encode_bin_request_new_pad); - gstelement_klass->release_pad = - GST_DEBUG_FUNCPTR (gst_encode_bin_release_pad); - - gst_element_class_set_static_metadata (gstelement_klass, - "Encoder Bin", - "Generic/Bin/Encoder", - "Convenience encoding/muxing element", - "Edward Hervey <edward.hervey@collabora.co.uk>"); -} - -static void -gst_encode_bin_dispose (GObject * object) -{ - GstEncodeBin *ebin = (GstEncodeBin *) object; - - if (ebin->muxers) - gst_plugin_feature_list_free (ebin->muxers); - ebin->muxers = NULL; - - if (ebin->formatters) - gst_plugin_feature_list_free (ebin->formatters); - ebin->formatters = NULL; - - if (ebin->encoders) - gst_plugin_feature_list_free (ebin->encoders); - ebin->encoders = NULL; - - if (ebin->parsers) - gst_plugin_feature_list_free (ebin->parsers); - ebin->parsers = NULL; - - gst_encode_bin_tear_down_profile (ebin); - - if (ebin->raw_video_caps) - gst_caps_unref (ebin->raw_video_caps); - ebin->raw_video_caps = NULL; - if (ebin->raw_audio_caps) - gst_caps_unref (ebin->raw_audio_caps); - ebin->raw_audio_caps = NULL; - /* if (ebin->raw_text_caps) */ - /* gst_caps_unref (ebin->raw_text_caps); */ - - G_OBJECT_CLASS (gst_encode_bin_parent_class)->dispose (object); -} - -static void -gst_encode_bin_init (GstEncodeBin * encode_bin) -{ - GstPadTemplate *tmpl; - - encode_bin->muxers = - gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_MUXER, - GST_RANK_MARGINAL); - - encode_bin->formatters = - gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_FORMATTER, - GST_RANK_SECONDARY); - - encode_bin->encoders = - gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_ENCODER, - GST_RANK_MARGINAL); - - encode_bin->parsers = - gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_PARSER, - GST_RANK_MARGINAL); - - encode_bin->raw_video_caps = gst_caps_from_string ("video/x-raw"); - encode_bin->raw_audio_caps = gst_caps_from_string ("audio/x-raw"); - /* encode_bin->raw_text_caps = */ - /* gst_caps_from_string ("text/x-raw"); */ - - encode_bin->queue_buffers_max = DEFAULT_QUEUE_BUFFERS_MAX; - encode_bin->queue_bytes_max = DEFAULT_QUEUE_BYTES_MAX; - encode_bin->queue_time_max = DEFAULT_QUEUE_TIME_MAX; - encode_bin->tolerance = DEFAULT_AUDIO_JITTER_TOLERANCE; - encode_bin->avoid_reencoding = DEFAULT_AVOID_REENCODING; - encode_bin->flags = DEFAULT_FLAGS; - - tmpl = gst_static_pad_template_get (&muxer_src_template); - encode_bin->srcpad = gst_ghost_pad_new_no_target_from_template ("src", tmpl); - gst_object_unref (tmpl); - gst_pad_set_active (encode_bin->srcpad, TRUE); - gst_element_add_pad (GST_ELEMENT_CAST (encode_bin), encode_bin->srcpad); -} - -static void -gst_encode_bin_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstEncodeBin *ebin = (GstEncodeBin *) object; - - switch (prop_id) { - case PROP_PROFILE: - gst_encode_bin_set_profile (ebin, - (GstEncodingProfile *) g_value_get_object (value)); - break; - case PROP_QUEUE_BUFFERS_MAX: - ebin->queue_buffers_max = g_value_get_uint (value); - break; - case PROP_QUEUE_BYTES_MAX: - ebin->queue_bytes_max = g_value_get_uint (value); - break; - case PROP_QUEUE_TIME_MAX: - ebin->queue_time_max = g_value_get_uint64 (value); - break; - case PROP_AUDIO_JITTER_TOLERANCE: - ebin->tolerance = g_value_get_uint64 (value); - break; - case PROP_AVOID_REENCODING: - ebin->avoid_reencoding = g_value_get_boolean (value); - break; - case PROP_FLAGS: - ebin->flags = g_value_get_flags (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_encode_bin_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstEncodeBin *ebin = (GstEncodeBin *) object; - - switch (prop_id) { - case PROP_PROFILE: - g_value_set_object (value, (GObject *) ebin->profile); - break; - case PROP_QUEUE_BUFFERS_MAX: - g_value_set_uint (value, ebin->queue_buffers_max); - break; - case PROP_QUEUE_BYTES_MAX: - g_value_set_uint (value, ebin->queue_bytes_max); - break; - case PROP_QUEUE_TIME_MAX: - g_value_set_uint64 (value, ebin->queue_time_max); - break; - case PROP_AUDIO_JITTER_TOLERANCE: - g_value_set_uint64 (value, ebin->tolerance); - break; - case PROP_AVOID_REENCODING: - g_value_set_boolean (value, ebin->avoid_reencoding); - break; - case PROP_FLAGS: - g_value_set_flags (value, ebin->flags); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static inline gboolean -are_raw_caps (const GstCaps * caps) -{ - GstCaps *raw = gst_static_caps_get (&default_raw_caps); - gboolean res = gst_caps_can_intersect (caps, raw); - - gst_caps_unref (raw); - return res; -} - -/* Returns the number of time a given stream profile is currently used - * in encodebin */ -static inline guint -stream_profile_used_count (GstEncodeBin * ebin, GstEncodingProfile * sprof) -{ - guint nbprofused = 0; - GList *tmp; - - for (tmp = ebin->streams; tmp; tmp = tmp->next) { - StreamGroup *sgroup = (StreamGroup *) tmp->data; - - if (sgroup->profile == sprof) - nbprofused++; - } - - return nbprofused; -} - -static inline GstEncodingProfile * -next_unused_stream_profile (GstEncodeBin * ebin, GType ptype, - const gchar * name, GstCaps * caps, GstEncodingProfile * previous_profile) -{ - GST_DEBUG_OBJECT (ebin, "ptype:%s, caps:%" GST_PTR_FORMAT, - g_type_name (ptype), caps); - - if (G_UNLIKELY (ptype == G_TYPE_NONE && caps != NULL)) { - /* Identify the profile type based on raw caps */ - if (gst_caps_can_intersect (ebin->raw_video_caps, caps)) - ptype = GST_TYPE_ENCODING_VIDEO_PROFILE; - else if (gst_caps_can_intersect (ebin->raw_audio_caps, caps)) - ptype = GST_TYPE_ENCODING_AUDIO_PROFILE; - /* else if (gst_caps_can_intersect (ebin->raw_text_caps, caps)) */ - /* ptype = GST_TYPE_ENCODING_TEXT_PROFILE; */ - GST_DEBUG_OBJECT (ebin, "Detected profile type as being %s", - g_type_name (ptype)); - } - - if (GST_IS_ENCODING_CONTAINER_PROFILE (ebin->profile)) { - const GList *tmp; - - if (name) { - /* If we have a name, try to find a profile with the same name */ - tmp = - gst_encoding_container_profile_get_profiles - (GST_ENCODING_CONTAINER_PROFILE (ebin->profile)); - - for (; tmp; tmp = tmp->next) { - GstEncodingProfile *sprof = (GstEncodingProfile *) tmp->data; - const gchar *profilename = gst_encoding_profile_get_name (sprof); - - if (profilename && !strcmp (name, profilename)) { - guint presence = gst_encoding_profile_get_presence (sprof); - - GST_DEBUG ("Found profile matching the requested name"); - - if (!gst_encoding_profile_is_enabled (sprof)) { - GST_INFO_OBJECT (ebin, "%p is disabled, not using it", sprof); - - return NULL; - } - - if (presence == 0 - || presence > stream_profile_used_count (ebin, sprof)) - return sprof; - - GST_WARNING ("Matching stream already used"); - return NULL; - } - } - GST_DEBUG - ("No profiles matching requested pad name, carrying on with normal stream matching"); - } - - for (tmp = - gst_encoding_container_profile_get_profiles - (GST_ENCODING_CONTAINER_PROFILE (ebin->profile)); tmp; - tmp = tmp->next) { - GstEncodingProfile *sprof = (GstEncodingProfile *) tmp->data; - - /* Pick an available Stream profile for which: - * * either it is of the compatible raw type, - * * OR we can pass it through directly without encoding - */ - if (G_TYPE_FROM_INSTANCE (sprof) == ptype) { - guint presence = gst_encoding_profile_get_presence (sprof); - GST_DEBUG ("Found a stream profile with the same type"); - if (!gst_encoding_profile_is_enabled (sprof)) { - GST_INFO_OBJECT (ebin, "%p is disabled, not using it", sprof); - } else if (presence == 0 - || (presence > stream_profile_used_count (ebin, sprof))) { - - if (sprof != previous_profile) - return sprof; - } - } else if (caps && ptype == G_TYPE_NONE) { - GstCaps *outcaps; - gboolean res; - - outcaps = gst_encoding_profile_get_input_caps (sprof); - GST_DEBUG ("Unknown stream, seeing if it's compatible with %" - GST_PTR_FORMAT, outcaps); - res = gst_caps_can_intersect (outcaps, caps); - gst_caps_unref (outcaps); - - if (res && sprof != previous_profile) - return sprof; - } - } - } - - return NULL; -} - -static GstPad * -request_pad_for_stream (GstEncodeBin * encodebin, GType ptype, - const gchar * name, GstCaps * caps) -{ - StreamGroup *sgroup = NULL; - GList *not_found_encoder_profs = NULL, *tmp; - GstEncodingProfile *sprof = NULL; - - GST_DEBUG_OBJECT (encodebin, "name:%s caps:%" GST_PTR_FORMAT, name, caps); - - while (sgroup == NULL) { - gboolean encoder_not_found = FALSE; - /* Figure out if we have a unused GstEncodingProfile we can use for - * these caps */ - sprof = next_unused_stream_profile (encodebin, ptype, name, caps, sprof); - - if (G_UNLIKELY (sprof == NULL)) - goto no_stream_profile; - - sgroup = _create_stream_group (encodebin, sprof, name, caps, - &encoder_not_found); - - if (G_UNLIKELY (sgroup)) - break; - - if (encoder_not_found) { - not_found_encoder_profs = g_list_prepend (not_found_encoder_profs, sprof); - if (name) { - GST_DEBUG ("Could not create an encoder for %s", name); - goto no_stream_group; - } - } else { - break; - } - } - - if (!sgroup) - goto no_stream_group; - - g_list_free (not_found_encoder_profs); - return sgroup->ghostpad; - -no_stream_profile: - { - GST_WARNING_OBJECT (encodebin, "Couldn't find a compatible stream profile"); - return NULL; - } - -no_stream_group: - { - for (tmp = not_found_encoder_profs; tmp; tmp = tmp->next) - _post_missing_plugin_message (encodebin, tmp->data); - g_list_free (not_found_encoder_profs); - - GST_WARNING_OBJECT (encodebin, "Couldn't create a StreamGroup"); - return NULL; - } -} - -static GstPad * -gst_encode_bin_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * name, const GstCaps * caps) -{ - GstEncodeBin *ebin = (GstEncodeBin *) element; - GstPad *res = NULL; - - GST_DEBUG_OBJECT (element, "templ:%s, name:%s", templ->name_template, name); - - /* Identify the stream group (if name or caps have been provided) */ - if (caps != NULL || name != NULL) { - res = request_pad_for_stream (ebin, G_TYPE_NONE, name, (GstCaps *) caps); - } - - if (res == NULL) { - GType ptype = G_TYPE_NONE; - - if (!strcmp (templ->name_template, "video_%u")) - ptype = GST_TYPE_ENCODING_VIDEO_PROFILE; - else if (!strcmp (templ->name_template, "audio_%u")) - ptype = GST_TYPE_ENCODING_AUDIO_PROFILE; - /* else if (!strcmp (templ->name_template, "text_%u")) */ - /* ptype = GST_TYPE_ENCODING_TEXT_PROFILE; */ - - /* FIXME : Check uniqueness of pad */ - /* FIXME : Check that the requested number is the last one, and if not, - * update the last_pad_id variable so that we don't create a pad with - * the same name/number in the future */ - - res = request_pad_for_stream (ebin, ptype, name, NULL); - } - - return res; -} - -static GstPad * -gst_encode_bin_request_pad_signal (GstEncodeBin * encodebin, GstCaps * caps) -{ - GstPad *pad = request_pad_for_stream (encodebin, G_TYPE_NONE, NULL, caps); - - return pad ? GST_PAD_CAST (gst_object_ref (pad)) : NULL; -} - -static GstPad * -gst_encode_bin_request_profile_pad_signal (GstEncodeBin * encodebin, - const gchar * profilename) -{ - GstPad *pad = - request_pad_for_stream (encodebin, G_TYPE_NONE, profilename, NULL); - - return pad ? GST_PAD_CAST (gst_object_ref (pad)) : NULL; -} - -static inline StreamGroup * -find_stream_group_from_pad (GstEncodeBin * ebin, GstPad * pad) -{ - GList *tmp; - - for (tmp = ebin->streams; tmp; tmp = tmp->next) { - StreamGroup *sgroup = (StreamGroup *) tmp->data; - if (G_UNLIKELY (sgroup->ghostpad == pad)) - return sgroup; - } - - return NULL; -} - -static void -gst_encode_bin_release_pad (GstElement * element, GstPad * pad) -{ - GstEncodeBin *ebin = (GstEncodeBin *) element; - StreamGroup *sgroup; - - /* Find the associated StreamGroup */ - - sgroup = find_stream_group_from_pad (ebin, pad); - if (G_UNLIKELY (sgroup == NULL)) - goto no_stream_group; - - /* Release objects/data associated with the StreamGroup */ - stream_group_remove (ebin, sgroup); - - return; - -no_stream_group: - { - GST_WARNING_OBJECT (ebin, "Couldn't find corresponding StreamGroup"); - return; - } -} - -/* Create a parser for the given stream profile */ -static inline GstElement * -_get_parser (GstEncodeBin * ebin, GstEncodingProfile * sprof) -{ - GList *parsers1, *parsers, *tmp; - GstElement *parser = NULL; - GstElementFactory *parserfact = NULL; - GstCaps *format; - - format = gst_encoding_profile_get_format (sprof); - - GST_DEBUG ("Getting list of parsers for format %" GST_PTR_FORMAT, format); - - /* FIXME : requesting twice the parsers twice is a bit ugly, we should - * have a method to request on more than one condition */ - parsers1 = - gst_element_factory_list_filter (ebin->parsers, format, - GST_PAD_SRC, FALSE); - parsers = - gst_element_factory_list_filter (parsers1, format, GST_PAD_SINK, FALSE); - gst_plugin_feature_list_free (parsers1); - - if (G_UNLIKELY (parsers == NULL)) { - GST_DEBUG ("Couldn't find any compatible parsers"); - goto beach; - } - - for (tmp = parsers; tmp; tmp = tmp->next) { - /* FIXME : We're only picking the first one so far */ - /* FIXME : signal the user if he wants this */ - parserfact = (GstElementFactory *) tmp->data; - break; - } - - if (parserfact) - parser = gst_element_factory_create (parserfact, NULL); - - gst_plugin_feature_list_free (parsers); - -beach: - if (format) - gst_caps_unref (format); - - return parser; -} - -static GstElement * -_create_element_and_set_preset (GstElementFactory * factory, - const gchar * preset, const gchar * name, const gchar * preset_name) -{ - GstElement *res = NULL; - - GST_DEBUG ("Creating element from factory %s (preset factory name: %s" - " preset name: %s)", GST_OBJECT_NAME (factory), preset_name, preset); - - if (preset_name && g_strcmp0 (GST_OBJECT_NAME (factory), preset_name)) { - GST_DEBUG ("Got to use %s, not %s", preset_name, GST_OBJECT_NAME (factory)); - return NULL; - } - - res = gst_element_factory_create (factory, name); - - if (preset && GST_IS_PRESET (res)) { - if (preset_name == NULL || - g_strcmp0 (GST_OBJECT_NAME (factory), preset_name) == 0) { - - if (!gst_preset_load_preset (GST_PRESET (res), preset)) { - GST_WARNING ("Couldn't set preset [%s] on element [%s]", - preset, GST_OBJECT_NAME (factory)); - gst_object_unref (res); - res = NULL; - } - } else { - GST_DEBUG ("Using a preset with no preset name, making use of the" - " proper element without setting any property"); - } - } - /* Else we keep it */ - - return res; -} - -/* Create the encoder for the given stream profile */ -static inline GstElement * -_get_encoder (GstEncodeBin * ebin, GstEncodingProfile * sprof) -{ - GList *encoders, *tmp; - GstElement *encoder = NULL; - GstElementFactory *encoderfact = NULL; - GstCaps *format; - const gchar *preset, *preset_name; - - format = gst_encoding_profile_get_format (sprof); - preset = gst_encoding_profile_get_preset (sprof); - preset_name = gst_encoding_profile_get_preset_name (sprof); - - GST_DEBUG ("Getting list of encoders for format %" GST_PTR_FORMAT, format); - - /* If stream caps are raw, return identity */ - if (G_UNLIKELY (are_raw_caps (format))) { - GST_DEBUG ("Stream format is raw, returning identity as the encoder"); - encoder = gst_element_factory_make ("identity", NULL); - goto beach; - } - - encoders = - gst_element_factory_list_filter (ebin->encoders, format, - GST_PAD_SRC, FALSE); - - if (G_UNLIKELY (encoders == NULL) && sprof == ebin->profile) { - /* Special case: if the top-level profile is an encoder, - * it could be listed in our muxers (for example wavenc) - */ - encoders = gst_element_factory_list_filter (ebin->muxers, format, - GST_PAD_SRC, FALSE); - } - - if (G_UNLIKELY (encoders == NULL)) { - GST_DEBUG ("Couldn't find any compatible encoders"); - goto beach; - } - - for (tmp = encoders; tmp; tmp = tmp->next) { - encoderfact = (GstElementFactory *) tmp->data; - if ((encoder = _create_element_and_set_preset (encoderfact, preset, - NULL, preset_name))) - break; - } - - gst_plugin_feature_list_free (encoders); - -beach: - if (format) - gst_caps_unref (format); - - return encoder; -} - -static GstPad * -local_element_request_pad (GstElement * element, GstPadTemplate * templ, - const gchar * name, const GstCaps * caps) -{ - GstPad *newpad = NULL; - GstElementClass *oclass; - - oclass = GST_ELEMENT_GET_CLASS (element); - - if (oclass->request_new_pad) - newpad = (oclass->request_new_pad) (element, templ, name, caps); - - if (newpad) - gst_object_ref (newpad); - - return newpad; -} - -static GstPad * -gst_element_get_pad_from_template (GstElement * element, GstPadTemplate * templ) -{ - GstPad *ret = NULL; - GstPadPresence presence; - - /* If this function is ever exported, we need check the validity of `element' - * and `templ', and to make sure the template actually belongs to the - * element. */ - - presence = GST_PAD_TEMPLATE_PRESENCE (templ); - - switch (presence) { - case GST_PAD_ALWAYS: - case GST_PAD_SOMETIMES: - ret = gst_element_get_static_pad (element, templ->name_template); - if (!ret && presence == GST_PAD_ALWAYS) - g_warning - ("Element %s has an ALWAYS template %s, but no pad of the same name", - GST_OBJECT_NAME (element), templ->name_template); - break; - - case GST_PAD_REQUEST: - ret = gst_element_request_pad (element, templ, NULL, NULL); - break; - } - - return ret; -} - -/* FIXME : Improve algorithm for finding compatible muxer sink pad */ -static inline GstPad * -get_compatible_muxer_sink_pad (GstEncodeBin * ebin, GstElement * encoder, - GstCaps * sinkcaps) -{ - GstPad *sinkpad; - GstPadTemplate *srctempl = NULL; - GstPadTemplate *sinktempl; - - if (encoder) { - GstPad *srcpad; - srcpad = gst_element_get_static_pad (encoder, "src"); - - srctempl = gst_pad_get_pad_template (srcpad); - - GST_DEBUG_OBJECT (ebin, - "Attempting to find pad from muxer %s compatible with %s:%s", - GST_ELEMENT_NAME (ebin->muxer), GST_DEBUG_PAD_NAME (srcpad)); - - gst_object_unref (srcpad); - sinktempl = gst_element_get_compatible_pad_template (ebin->muxer, srctempl); - gst_object_unref (srctempl); - } else { - srctempl = - gst_pad_template_new ("whatever", GST_PAD_SRC, GST_PAD_ALWAYS, - sinkcaps); - g_assert (srctempl != NULL); - sinktempl = gst_element_get_compatible_pad_template (ebin->muxer, srctempl); - gst_object_unref (srctempl); - } - - if (G_UNLIKELY (sinktempl == NULL)) - goto no_template; - - sinkpad = gst_element_get_pad_from_template (ebin->muxer, sinktempl); - - return sinkpad; - -no_template: - { - GST_WARNING_OBJECT (ebin, "No compatible pad available on muxer"); - return NULL; - } -} - -static gboolean -_has_class (GstElement * element, const gchar * classname) -{ - GstElementClass *klass; - const gchar *value; - - klass = GST_ELEMENT_GET_CLASS (element); - value = gst_element_class_get_metadata (klass, GST_ELEMENT_METADATA_KLASS); - if (!value) - return FALSE; - - return strstr (value, classname) != NULL; -} - -static void -_profile_restriction_caps_cb (GstEncodingProfile * profile, - GParamSpec * arg G_GNUC_UNUSED, StreamGroup * group) -{ - GstCaps *restriction = gst_encoding_profile_get_restriction (profile); - - g_object_set (group->capsfilter, "caps", restriction, NULL); -} - -static void -_capsfilter_force_format (GstPad * pad, - GParamSpec * arg G_GNUC_UNUSED, gulong * signal_id) -{ - GstCaps *caps; - GstStructure *structure; - - g_object_get (pad, "caps", &caps, NULL); - caps = gst_caps_copy (caps); - - structure = gst_caps_get_structure (caps, 0); - gst_structure_remove_field (structure, "streamheader"); - GST_INFO_OBJECT (pad, "Forcing caps to %" GST_PTR_FORMAT, caps); - g_object_set (GST_OBJECT_PARENT (pad), "caps", caps, NULL); - g_signal_handler_disconnect (pad, *signal_id); - *signal_id = 0; - gst_caps_unref (caps); -} - -static void -_set_group_caps_format (StreamGroup * sgroup, GstEncodingProfile * prof, - GstCaps * format) -{ - g_object_set (sgroup->outfilter, "caps", format, NULL); - - if (!gst_encoding_profile_get_allow_dynamic_output (prof)) { - if (!sgroup->outputfilter_caps_sid) { - sgroup->outputfilter_caps_sid = - g_signal_connect (sgroup->outfilter->sinkpads->data, - "notify::caps", G_CALLBACK (_capsfilter_force_format), - &sgroup->outputfilter_caps_sid); - } - } -} - -static void -_post_missing_plugin_message (GstEncodeBin * ebin, GstEncodingProfile * prof) -{ - GstCaps *format; - format = gst_encoding_profile_get_format (prof); - - GST_ERROR_OBJECT (ebin, - "Couldn't create encoder with preset %s and preset name %s" - " for format %" GST_PTR_FORMAT, - GST_STR_NULL (gst_encoding_profile_get_preset (prof)), - GST_STR_NULL (gst_encoding_profile_get_preset_name (prof)), format); - - /* missing plugin support */ - gst_element_post_message (GST_ELEMENT_CAST (ebin), - gst_missing_encoder_message_new (GST_ELEMENT_CAST (ebin), format)); - GST_ELEMENT_ERROR (ebin, CORE, MISSING_PLUGIN, - ("Couldn't create encoder for format %" GST_PTR_FORMAT, format), (NULL)); - - gst_caps_unref (format); -} - -static GstPadProbeReturn -_missing_plugin_probe (GstPad * pad, GstPadProbeInfo * info, gpointer udata) -{ - StreamGroup *sgroup = udata; - GstEncodeBin *ebin = sgroup->ebin; - - _post_missing_plugin_message (ebin, sgroup->profile); - - return GST_PAD_PROBE_OK; -} - -static void -_set_up_fake_encoder_pad_probe (GstEncodeBin * ebin, StreamGroup * sgroup) -{ - GstPad *pad = gst_element_get_static_pad (sgroup->fakesink, "sink"); - - gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER, _missing_plugin_probe, - sgroup, NULL); - - gst_object_unref (pad); -} - -/* FIXME : Add handling of streams that don't require conversion elements */ -/* - * Create the elements, StreamGroup, add the sink pad, link it to the muxer - * - * sinkpadname: If non-NULL, that name will be assigned to the sink ghost pad - * sinkcaps: If non-NULL will be used to figure out how to setup the group - * encoder_not_found: If non NULL, set to TRUE if failure happened because - * the encoder could not be found - */ -static StreamGroup * -_create_stream_group (GstEncodeBin * ebin, GstEncodingProfile * sprof, - const gchar * sinkpadname, GstCaps * sinkcaps, gboolean * encoder_not_found) -{ - StreamGroup *sgroup = NULL; - GstPad *sinkpad, *srcpad = NULL, *muxerpad = NULL; - /* Element we will link to the encoder */ - GstElement *last = NULL; - GstElement *encoder = NULL; - GList *tmp, *tosync = NULL; - GstCaps *format, *restriction; - const gchar *missing_element_name; - - format = gst_encoding_profile_get_format (sprof); - restriction = gst_encoding_profile_get_restriction (sprof); - - GST_DEBUG ("Creating group. format %" GST_PTR_FORMAT ", for caps %" - GST_PTR_FORMAT, format, sinkcaps); - GST_DEBUG ("avoid_reencoding:%d", ebin->avoid_reencoding); - - sgroup = g_slice_new0 (StreamGroup); - sgroup->ebin = ebin; - sgroup->profile = sprof; - - /* NOTE for people reading this code: - * - * We construct the group starting by the furthest downstream element - * and making our way up adding/syncing/linking as we go. - * - * There are two parallel paths: - * * One for raw data which goes through converters and encoders - * * One for already encoded data - */ - - /* Muxer. - * If we are handling a container profile, figure out if the muxer has a - * sinkpad compatible with the selected profile */ - if (ebin->muxer) { - muxerpad = get_compatible_muxer_sink_pad (ebin, NULL, format); - if (G_UNLIKELY (muxerpad == NULL)) - goto no_muxer_pad; - - } - - /* Output Queue. - * The actual queueing will be done in the input queue, but some queuing - * after the encoder can be beneficial for encoding performance. */ - last = sgroup->outqueue = gst_element_factory_make ("queue", NULL); - g_object_set (sgroup->outqueue, "max-size-buffers", (guint) 0, - "max-size-bytes", (guint) 0, "max-size-time", (guint64) 3 * GST_SECOND, - "silent", TRUE, NULL); - - gst_bin_add (GST_BIN (ebin), sgroup->outqueue); - tosync = g_list_append (tosync, sgroup->outqueue); - srcpad = gst_element_get_static_pad (sgroup->outqueue, "src"); - if (muxerpad) { - if (G_UNLIKELY (fast_pad_link (srcpad, muxerpad) != GST_PAD_LINK_OK)) { - goto muxer_link_failure; - } - gst_object_unref (muxerpad); - } else { - gst_ghost_pad_set_target (GST_GHOST_PAD (ebin->srcpad), srcpad); - } - gst_object_unref (srcpad); - srcpad = NULL; - - /* Check if we need a formatter - * If we have no muxer or - * if the muxer isn't a formatter and doesn't implement the tagsetter interface - */ - if (!ebin->muxer || (!GST_IS_TAG_SETTER (ebin->muxer) - && !_has_class (ebin->muxer, "Formatter"))) { - sgroup->formatter = _get_formatter (ebin, sprof); - if (sgroup->formatter) { - GST_DEBUG ("Adding formatter for %" GST_PTR_FORMAT, format); - - gst_bin_add (GST_BIN (ebin), sgroup->formatter); - tosync = g_list_append (tosync, sgroup->formatter); - if (G_UNLIKELY (!fast_element_link (sgroup->formatter, last))) - goto formatter_link_failure; - last = sgroup->formatter; - } - } - - - /* Output capsfilter - * This will receive the format caps from the streamprofile */ - GST_DEBUG ("Adding output capsfilter for %" GST_PTR_FORMAT, format); - sgroup->outfilter = gst_element_factory_make ("capsfilter", NULL); - _set_group_caps_format (sgroup, sprof, format); - - gst_bin_add (GST_BIN (ebin), sgroup->outfilter); - tosync = g_list_append (tosync, sgroup->outfilter); - if (G_UNLIKELY (!fast_element_link (sgroup->outfilter, last))) - goto outfilter_link_failure; - last = sgroup->outfilter; - - - sgroup->parser = _get_parser (ebin, sprof); - - if (sgroup->parser != NULL) { - GST_DEBUG ("Got a parser %s", GST_ELEMENT_NAME (sgroup->parser)); - gst_bin_add (GST_BIN (ebin), sgroup->parser); - tosync = g_list_append (tosync, sgroup->parser); - if (G_UNLIKELY (!gst_element_link (sgroup->parser, last))) - goto parser_link_failure; - last = sgroup->parser; - } - - /* Stream combiner */ - sgroup->combiner = g_object_new (GST_TYPE_STREAM_COMBINER, NULL); - - gst_bin_add (GST_BIN (ebin), sgroup->combiner); - tosync = g_list_append (tosync, sgroup->combiner); - if (G_UNLIKELY (!fast_element_link (sgroup->combiner, last))) - goto combiner_link_failure; - - - /* Stream splitter */ - sgroup->splitter = g_object_new (GST_TYPE_STREAM_SPLITTER, NULL); - - gst_bin_add (GST_BIN (ebin), sgroup->splitter); - tosync = g_list_append (tosync, sgroup->splitter); - - /* Input queue - * FIXME : figure out what max-size to use for the input queue */ - sgroup->inqueue = gst_element_factory_make ("queue", NULL); - g_object_set (sgroup->inqueue, "max-size-buffers", - (guint) ebin->queue_buffers_max, "max-size-bytes", - (guint) ebin->queue_bytes_max, "max-size-time", - (guint64) ebin->queue_time_max, "silent", TRUE, NULL); - - gst_bin_add (GST_BIN (ebin), sgroup->inqueue); - tosync = g_list_append (tosync, sgroup->inqueue); - if (G_UNLIKELY (!fast_element_link (sgroup->inqueue, sgroup->splitter))) - goto splitter_link_failure; - - /* Expose input queue sink pad as ghostpad */ - sinkpad = gst_element_get_static_pad (sgroup->inqueue, "sink"); - if (sinkpadname == NULL) { - gchar *pname = - g_strdup_printf ("%s_%u", gst_encoding_profile_get_type_nick (sprof), - ebin->last_pad_id++); - GST_DEBUG ("Adding ghost pad %s", pname); - sgroup->ghostpad = gst_ghost_pad_new (pname, sinkpad); - g_free (pname); - } else - sgroup->ghostpad = gst_ghost_pad_new (sinkpadname, sinkpad); - gst_object_unref (sinkpad); - - - /* Path 1 : Already-encoded data */ - sinkpad = - local_element_request_pad (sgroup->combiner, NULL, "passthroughsink", - NULL); - if (G_UNLIKELY (sinkpad == NULL)) - goto no_combiner_sinkpad; - - if (ebin->avoid_reencoding) { - GstCaps *tmpcaps; - - GST_DEBUG ("Asked to use Smart Encoder"); - sgroup->smartencoder = g_object_new (GST_TYPE_SMART_ENCODER, NULL); - - /* Check if stream format is compatible */ - srcpad = gst_element_get_static_pad (sgroup->smartencoder, "src"); - tmpcaps = gst_pad_query_caps (srcpad, NULL); - if (!gst_caps_can_intersect (tmpcaps, format)) { - GST_DEBUG ("We don't have a smart encoder for the stream format"); - gst_object_unref (sgroup->smartencoder); - sgroup->smartencoder = NULL; - } else { - gst_bin_add ((GstBin *) ebin, sgroup->smartencoder); - fast_pad_link (srcpad, sinkpad); - tosync = g_list_append (tosync, sgroup->smartencoder); - sinkpad = gst_element_get_static_pad (sgroup->smartencoder, "sink"); - } - gst_caps_unref (tmpcaps); - gst_object_unref (srcpad); - } - - srcpad = - local_element_request_pad (sgroup->splitter, NULL, "passthroughsrc", - NULL); - if (G_UNLIKELY (srcpad == NULL)) - goto no_splitter_srcpad; - - /* Go straight to splitter */ - if (G_UNLIKELY (fast_pad_link (srcpad, sinkpad) != GST_PAD_LINK_OK)) - goto passthrough_link_failure; - gst_object_unref (sinkpad); - gst_object_unref (srcpad); - srcpad = NULL; - - /* Path 2 : Conversion / Encoding */ - - /* 1. Create the encoder */ - GST_LOG ("Adding encoder"); - sgroup->encoder = _get_encoder (ebin, sprof); - if (sgroup->encoder != NULL) { - gst_bin_add ((GstBin *) ebin, sgroup->encoder); - tosync = g_list_append (tosync, sgroup->encoder); - - sinkpad = - local_element_request_pad (sgroup->combiner, NULL, "encodingsink", - NULL); - if (G_UNLIKELY (sinkpad == NULL)) - goto no_combiner_sinkpad; - srcpad = gst_element_get_static_pad (sgroup->encoder, "src"); - if (G_UNLIKELY (fast_pad_link (srcpad, sinkpad) != GST_PAD_LINK_OK)) - goto encoder_link_failure; - gst_object_unref (sinkpad); - gst_object_unref (srcpad); - srcpad = NULL; - } else if (gst_encoding_profile_get_preset (sgroup->profile) - || gst_encoding_profile_get_preset_name (sgroup->profile)) { - - if (!encoder_not_found) - _post_missing_plugin_message (ebin, sprof); - else - *encoder_not_found = TRUE; - goto cleanup; - } else { - /* passthrough can still work, if we discover that * - * encoding is required we post a missing plugin message */ - } - - - /* 3. Create the conversion/restriction elements */ - /* 3.1. capsfilter */ - GST_LOG ("Adding capsfilter for restriction caps : %" GST_PTR_FORMAT, - restriction); - - last = sgroup->capsfilter = gst_element_factory_make ("capsfilter", NULL); - if (restriction && !gst_caps_is_any (restriction)) - g_object_set (sgroup->capsfilter, "caps", restriction, NULL); - - if (!gst_encoding_profile_get_allow_dynamic_output (sprof)) { - if (!sgroup->inputfilter_caps_sid) { - sgroup->inputfilter_caps_sid = - g_signal_connect (sgroup->capsfilter->sinkpads->data, - "notify::caps", G_CALLBACK (_capsfilter_force_format), - &sgroup->inputfilter_caps_sid); - } - } - - gst_bin_add ((GstBin *) ebin, sgroup->capsfilter); - tosync = g_list_append (tosync, sgroup->capsfilter); - if (sgroup->encoder == NULL) { - /* no encoder available but it might be possible to just do passthrough, so - * let's just set up a fake pad to detect that encoding was attempted and - * if so it posts the missing plugin message */ - sgroup->fakesink = gst_element_factory_make ("fakesink", NULL); - g_object_set (sgroup->fakesink, "async", FALSE, NULL); - gst_bin_add (GST_BIN_CAST (ebin), sgroup->fakesink); - tosync = g_list_append (tosync, sgroup->fakesink); - encoder = sgroup->fakesink; - - _set_up_fake_encoder_pad_probe (ebin, sgroup); - } else { - encoder = sgroup->encoder; - } - fast_element_link (sgroup->capsfilter, encoder); - sgroup->restriction_sid = g_signal_connect (sprof, "notify::restriction-caps", - G_CALLBACK (_profile_restriction_caps_cb), sgroup); - - /* 3.2. restriction elements */ - /* FIXME : Once we have properties for specific converters, use those */ - if (GST_IS_ENCODING_VIDEO_PROFILE (sprof)) { - const gboolean native_video = - ! !(ebin->flags & GST_ENCODEBIN_FLAG_NO_VIDEO_CONVERSION); - GstElement *cspace = NULL, *scale, *vrate, *cspace2 = NULL; - - GST_LOG ("Adding conversion elements for video stream"); - - if (!native_video) { - cspace = gst_element_factory_make ("videoconvert", NULL); - scale = gst_element_factory_make ("videoscale", NULL); - if (!scale) { - missing_element_name = "videoscale"; - goto missing_element; - } - /* 4-tap scaling and black borders */ - g_object_set (scale, "method", 2, "add-borders", TRUE, NULL); - cspace2 = gst_element_factory_make ("videoconvert", NULL); - - if (!cspace || !cspace2) { - missing_element_name = "videoconvert"; - goto missing_element; - } - - gst_bin_add_many ((GstBin *) ebin, cspace, scale, cspace2, NULL); - tosync = g_list_append (tosync, cspace); - tosync = g_list_append (tosync, scale); - tosync = g_list_append (tosync, cspace2); - - sgroup->converters = g_list_prepend (sgroup->converters, cspace); - sgroup->converters = g_list_prepend (sgroup->converters, scale); - sgroup->converters = g_list_prepend (sgroup->converters, cspace2); - - if (!fast_element_link (cspace, scale) || - !fast_element_link (scale, cspace2)) - goto converter_link_failure; - } - - if (!gst_encoding_video_profile_get_variableframerate - (GST_ENCODING_VIDEO_PROFILE (sprof))) { - vrate = gst_element_factory_make ("videorate", NULL); - if (!vrate) { - missing_element_name = "videorate"; - goto missing_element; - } - g_object_set (vrate, "skip-to-first", TRUE, NULL); - - gst_bin_add ((GstBin *) ebin, vrate); - tosync = g_list_prepend (tosync, vrate); - sgroup->converters = g_list_prepend (sgroup->converters, vrate); - - if ((!native_video && !fast_element_link (cspace2, vrate)) - || !fast_element_link (vrate, last)) - goto converter_link_failure; - - if (!native_video) - last = cspace; - else - last = vrate; - } else if (!native_video) { - if (!fast_element_link (cspace2, last)) - goto converter_link_failure; - last = cspace; - } - - } else if (GST_IS_ENCODING_AUDIO_PROFILE (sprof) - && !(ebin->flags & GST_ENCODEBIN_FLAG_NO_AUDIO_CONVERSION)) { - GstElement *aconv, *ares, *arate, *aconv2; - - GST_LOG ("Adding conversion elements for audio stream"); - - arate = gst_element_factory_make ("audiorate", NULL); - if (!arate) { - missing_element_name = "audiorate"; - goto missing_element; - } - g_object_set (arate, "tolerance", (guint64) ebin->tolerance, NULL); - g_object_set (arate, "skip-to-first", TRUE, NULL); - - aconv = gst_element_factory_make ("audioconvert", NULL); - aconv2 = gst_element_factory_make ("audioconvert", NULL); - ares = gst_element_factory_make ("audioresample", NULL); - if (!aconv || !aconv2) { - missing_element_name = "audioconvert"; - goto missing_element; - } - if (!ares) { - missing_element_name = "audioresample"; - goto missing_element; - } - - gst_bin_add_many ((GstBin *) ebin, arate, aconv, ares, aconv2, NULL); - tosync = g_list_append (tosync, arate); - tosync = g_list_append (tosync, aconv); - tosync = g_list_append (tosync, ares); - tosync = g_list_append (tosync, aconv2); - if (!fast_element_link (arate, aconv) || - !fast_element_link (aconv, ares) || - !fast_element_link (ares, aconv2) || !fast_element_link (aconv2, last)) - goto converter_link_failure; - - sgroup->converters = g_list_prepend (sgroup->converters, arate); - sgroup->converters = g_list_prepend (sgroup->converters, aconv); - sgroup->converters = g_list_prepend (sgroup->converters, ares); - sgroup->converters = g_list_prepend (sgroup->converters, aconv2); - - last = arate; - } - - /* Link to stream splitter */ - sinkpad = gst_element_get_static_pad (last, "sink"); - srcpad = - local_element_request_pad (sgroup->splitter, NULL, "encodingsrc", NULL); - if (G_UNLIKELY (srcpad == NULL)) - goto no_splitter_srcpad; - if (G_UNLIKELY (fast_pad_link (srcpad, sinkpad) != GST_PAD_LINK_OK)) - goto splitter_encoding_failure; - gst_object_unref (sinkpad); - gst_object_unref (srcpad); - srcpad = NULL; - - /* End of Stream 2 setup */ - - /* Sync all elements to parent state */ - for (tmp = tosync; tmp; tmp = tmp->next) - gst_element_sync_state_with_parent ((GstElement *) tmp->data); - g_list_free (tosync); - - /* Add ghostpad */ - GST_DEBUG ("Adding ghostpad %s:%s", GST_DEBUG_PAD_NAME (sgroup->ghostpad)); - gst_pad_set_active (sgroup->ghostpad, TRUE); - gst_element_add_pad ((GstElement *) ebin, sgroup->ghostpad); - - /* Add StreamGroup to our list of streams */ - - GST_DEBUG - ("Done creating elements, adding StreamGroup to our controlled stream list"); - - ebin->streams = g_list_prepend (ebin->streams, sgroup); - - if (format) - gst_caps_unref (format); - if (restriction) - gst_caps_unref (restriction); - - return sgroup; - -splitter_encoding_failure: - GST_ERROR_OBJECT (ebin, "Error linking splitter to encoding stream"); - goto cleanup; - -no_muxer_pad: - GST_ERROR_OBJECT (ebin, - "Couldn't find a compatible muxer pad to link encoder to"); - goto cleanup; - -missing_element: - gst_element_post_message (GST_ELEMENT_CAST (ebin), - gst_missing_element_message_new (GST_ELEMENT_CAST (ebin), - missing_element_name)); - GST_ELEMENT_ERROR (ebin, CORE, MISSING_PLUGIN, - (_("Missing element '%s' - check your GStreamer installation."), - missing_element_name), (NULL)); - goto cleanup; - -encoder_link_failure: - GST_ERROR_OBJECT (ebin, "Failed to link the encoder"); - goto cleanup; - -muxer_link_failure: - GST_ERROR_OBJECT (ebin, "Couldn't link encoder to muxer"); - goto cleanup; - -formatter_link_failure: - GST_ERROR_OBJECT (ebin, "Couldn't link output filter to output queue"); - goto cleanup; - -outfilter_link_failure: - GST_ERROR_OBJECT (ebin, - "Couldn't link output filter to output queue/formatter"); - goto cleanup; - -passthrough_link_failure: - GST_ERROR_OBJECT (ebin, "Failed linking splitter in passthrough mode"); - goto cleanup; - -no_splitter_srcpad: - GST_ERROR_OBJECT (ebin, "Couldn't get a source pad from the splitter"); - goto cleanup; - -no_combiner_sinkpad: - GST_ERROR_OBJECT (ebin, "Couldn't get a sink pad from the combiner"); - goto cleanup; - -splitter_link_failure: - GST_ERROR_OBJECT (ebin, "Failure linking to the splitter"); - goto cleanup; - -combiner_link_failure: - GST_ERROR_OBJECT (ebin, "Failure linking to the combiner"); - goto cleanup; - -parser_link_failure: - GST_ERROR_OBJECT (ebin, "Failure linking the parser"); - goto cleanup; - -converter_link_failure: - GST_ERROR_OBJECT (ebin, "Failure linking the video converters"); - goto cleanup; - -cleanup: - /* FIXME : Actually properly cleanup everything */ - if (format) - gst_caps_unref (format); - if (restriction) - gst_caps_unref (restriction); - if (srcpad) - gst_object_unref (srcpad); - stream_group_free (ebin, sgroup); - g_list_free (tosync); - return NULL; -} - -static gboolean -_gst_caps_match_foreach (GQuark field_id, const GValue * value, gpointer data) -{ - GstStructure *structure = data; - const GValue *other_value = gst_structure_id_get_value (structure, field_id); - - if (G_UNLIKELY (other_value == NULL)) - return FALSE; - if (gst_value_compare (value, other_value) == GST_VALUE_EQUAL) { - return TRUE; - } - - return FALSE; -} - -/* - * checks that there is at least one structure on caps_a that has - * all its fields exactly the same as one structure on caps_b - */ -static gboolean -_gst_caps_match (const GstCaps * caps_a, const GstCaps * caps_b) -{ - gint i, j; - gboolean res = FALSE; - - for (i = 0; i < gst_caps_get_size (caps_a); i++) { - GstStructure *structure_a = gst_caps_get_structure (caps_a, i); - for (j = 0; j < gst_caps_get_size (caps_b); j++) { - GstStructure *structure_b = gst_caps_get_structure (caps_b, j); - - res = gst_structure_foreach (structure_a, _gst_caps_match_foreach, - structure_b); - if (res) - goto end; - } - } -end: - return res; -} - -static gboolean -_factory_can_handle_caps (GstElementFactory * factory, const GstCaps * caps, - GstPadDirection dir, gboolean exact) -{ - const GList *templates; - - templates = gst_element_factory_get_static_pad_templates (factory); - while (templates) { - GstStaticPadTemplate *template = (GstStaticPadTemplate *) templates->data; - - if (template->direction == dir) { - GstCaps *tmp = gst_static_caps_get (&template->static_caps); - - if ((exact && _gst_caps_match (caps, tmp)) || - (!exact && gst_caps_can_intersect (tmp, caps))) { - gst_caps_unref (tmp); - return TRUE; - } - gst_caps_unref (tmp); - } - templates = g_list_next (templates); - } - - return FALSE; -} - -static inline GstElement * -_get_formatter (GstEncodeBin * ebin, GstEncodingProfile * sprof) -{ - GList *formatters, *tmpfmtr; - GstElement *formatter = NULL; - GstElementFactory *formatterfact = NULL; - GstCaps *format; - const gchar *preset, *preset_name; - - format = gst_encoding_profile_get_format (sprof); - preset = gst_encoding_profile_get_preset (sprof); - preset_name = gst_encoding_profile_get_preset_name (sprof); - - GST_DEBUG ("Getting list of formatters for format %" GST_PTR_FORMAT, format); - - formatters = - gst_element_factory_list_filter (ebin->formatters, format, GST_PAD_SRC, - FALSE); - - if (formatters == NULL) - goto beach; - - /* FIXME : signal the user if he wants this */ - for (tmpfmtr = formatters; tmpfmtr; tmpfmtr = tmpfmtr->next) { - formatterfact = (GstElementFactory *) tmpfmtr->data; - - GST_DEBUG_OBJECT (ebin, "Trying formatter %s", - GST_OBJECT_NAME (formatterfact)); - - if ((formatter = - _create_element_and_set_preset (formatterfact, preset, - NULL, preset_name))) - break; - } - - gst_plugin_feature_list_free (formatters); - -beach: - if (format) - gst_caps_unref (format); - return formatter; -} - -static gint -compare_elements (gconstpointer a, gconstpointer b, gpointer udata) -{ - GstCaps *caps = udata; - GstElementFactory *fac_a = (GstElementFactory *) a; - GstElementFactory *fac_b = (GstElementFactory *) b; - - /* FIXME not quite sure this is the best algorithm to order the elements - * Some caps similarity comparison algorithm would fit better than going - * boolean (equals/not equals). - */ - gboolean equals_a = _factory_can_handle_caps (fac_a, caps, GST_PAD_SRC, TRUE); - gboolean equals_b = _factory_can_handle_caps (fac_b, caps, GST_PAD_SRC, TRUE); - - if (equals_a == equals_b) { - return gst_plugin_feature_get_rank ((GstPluginFeature *) fac_b) - - gst_plugin_feature_get_rank ((GstPluginFeature *) fac_a); - } else if (equals_a) { - return -1; - } else if (equals_b) { - return 1; - } - return 0; -} - -static inline GstElement * -_get_muxer (GstEncodeBin * ebin) -{ - GList *muxers, *formatters, *tmpmux; - GstElement *muxer = NULL; - GstElementFactory *muxerfact = NULL; - const GList *tmp; - GstCaps *format; - const gchar *preset, *preset_name; - - format = gst_encoding_profile_get_format (ebin->profile); - preset = gst_encoding_profile_get_preset (ebin->profile); - preset_name = gst_encoding_profile_get_preset_name (ebin->profile); - - GST_DEBUG ("Getting list of muxers for format %" GST_PTR_FORMAT, format); - - muxers = - gst_element_factory_list_filter (ebin->muxers, format, GST_PAD_SRC, TRUE); - - formatters = - gst_element_factory_list_filter (ebin->formatters, format, GST_PAD_SRC, - TRUE); - - muxers = g_list_sort_with_data (muxers, compare_elements, (gpointer) format); - formatters = - g_list_sort_with_data (formatters, compare_elements, (gpointer) format); - - muxers = g_list_concat (muxers, formatters); - - if (muxers == NULL) - goto beach; - - /* FIXME : signal the user if he wants this */ - for (tmpmux = muxers; tmpmux; tmpmux = tmpmux->next) { - gboolean cansinkstreams = TRUE; - const GList *profiles = - gst_encoding_container_profile_get_profiles - (GST_ENCODING_CONTAINER_PROFILE (ebin->profile)); - - muxerfact = (GstElementFactory *) tmpmux->data; - - GST_DEBUG ("Trying muxer %s", GST_OBJECT_NAME (muxerfact)); - - /* See if the muxer can sink all of our stream profile caps */ - for (tmp = profiles; tmp; tmp = tmp->next) { - GstEncodingProfile *sprof = (GstEncodingProfile *) tmp->data; - GstCaps *sformat = gst_encoding_profile_get_format (sprof); - - if (!_factory_can_handle_caps (muxerfact, sformat, GST_PAD_SINK, FALSE)) { - GST_DEBUG ("Skipping muxer because it can't sink caps %" - GST_PTR_FORMAT, sformat); - cansinkstreams = FALSE; - if (sformat) - gst_caps_unref (sformat); - break; - } - if (sformat) - gst_caps_unref (sformat); - } - - /* Only use a muxer than can use all streams and than can accept the - * preset (which may be present or not) */ - if (cansinkstreams && (muxer = - _create_element_and_set_preset (muxerfact, preset, "muxer", - preset_name))) - break; - } - - gst_plugin_feature_list_free (muxers); - -beach: - if (format) - gst_caps_unref (format); - return muxer; -} - -static gboolean -create_elements_and_pads (GstEncodeBin * ebin) -{ - gboolean ret = TRUE; - GstElement *muxer = NULL; - GstPad *muxerpad; - const GList *tmp, *profiles; - GstEncodingProfile *sprof; - - GST_DEBUG ("Current profile : %s", - gst_encoding_profile_get_name (ebin->profile)); - - if (GST_IS_ENCODING_CONTAINER_PROFILE (ebin->profile)) { - /* 1. Get the compatible muxer */ - muxer = _get_muxer (ebin); - if (G_UNLIKELY (muxer == NULL)) - goto no_muxer; - - /* Record the muxer */ - ebin->muxer = muxer; - gst_bin_add ((GstBin *) ebin, muxer); - - /* 2. Ghost the muxer source pad */ - - /* FIXME : We should figure out if it's a static/request/dyamic pad, - * but for the time being let's assume it's a static pad :) */ - muxerpad = gst_element_get_static_pad (muxer, "src"); - if (G_UNLIKELY (muxerpad == NULL)) - goto no_muxer_pad; - - if (!gst_ghost_pad_set_target (GST_GHOST_PAD (ebin->srcpad), muxerpad)) - goto no_muxer_ghost_pad; - - gst_object_unref (muxerpad); - /* 3. Activate fixed presence streams */ - profiles = - gst_encoding_container_profile_get_profiles - (GST_ENCODING_CONTAINER_PROFILE (ebin->profile)); - for (tmp = profiles; tmp; tmp = tmp->next) { - sprof = (GstEncodingProfile *) tmp->data; - - GST_DEBUG ("Trying stream profile with presence %d", - gst_encoding_profile_get_presence (sprof)); - - if (gst_encoding_profile_get_presence (sprof) != 0 && - gst_encoding_profile_is_enabled (sprof)) { - if (G_UNLIKELY (_create_stream_group (ebin, sprof, NULL, NULL, - NULL) == NULL)) - goto stream_error; - } - } - gst_element_sync_state_with_parent (muxer); - } else { - if (G_UNLIKELY (_create_stream_group (ebin, ebin->profile, NULL, - NULL, NULL) == NULL)) - goto stream_error; - } - - return ret; - -no_muxer: - { - GstCaps *format = gst_encoding_profile_get_format (ebin->profile); - - GST_WARNING ("No available muxer for %" GST_PTR_FORMAT, format); - /* missing plugin support */ - gst_element_post_message (GST_ELEMENT_CAST (ebin), - gst_missing_encoder_message_new (GST_ELEMENT_CAST (ebin), format)); - GST_ELEMENT_ERROR (ebin, CORE, MISSING_PLUGIN, - ("No available muxer for format %" GST_PTR_FORMAT, format), (NULL)); - if (format) - gst_caps_unref (format); - return FALSE; - } - -no_muxer_pad: - { - GST_WARNING ("Can't get source pad from muxer (%s)", - GST_ELEMENT_NAME (muxer)); - gst_bin_remove (GST_BIN (ebin), muxer); - return FALSE; - } - -no_muxer_ghost_pad: - { - GST_WARNING ("Couldn't set %s:%s as source ghostpad target", - GST_DEBUG_PAD_NAME (muxerpad)); - gst_bin_remove (GST_BIN (ebin), muxer); - gst_object_unref (muxerpad); - return FALSE; - } - -stream_error: - { - GST_WARNING ("Could not create Streams"); - if (muxer) - gst_bin_remove (GST_BIN (ebin), muxer); - ebin->muxer = NULL; - return FALSE; - } -} - -static void -release_pads (const GValue * item, GstElement * elt) -{ - GstPad *pad = g_value_get_object (item); - GstPad *peer = NULL; - - GST_DEBUG_OBJECT (elt, "Releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - - /* Unlink from its peer pad */ - if ((peer = gst_pad_get_peer (pad))) { - if (GST_PAD_DIRECTION (peer) == GST_PAD_SRC) - gst_pad_unlink (peer, pad); - else - gst_pad_unlink (pad, peer); - gst_object_unref (peer); - } - - /* Release it from the object */ - gst_element_release_request_pad (elt, pad); -} - -static void -stream_group_free (GstEncodeBin * ebin, StreamGroup * sgroup) -{ - GList *tmp; - GstPad *tmppad; - GstPad *pad; - - GST_DEBUG_OBJECT (ebin, "Freeing StreamGroup %p", sgroup); - - if (sgroup->restriction_sid != 0) - g_signal_handler_disconnect (sgroup->profile, sgroup->restriction_sid); - - if (sgroup->outqueue) { - if (ebin->muxer) { - /* outqueue - Muxer */ - tmppad = gst_element_get_static_pad (sgroup->outqueue, "src"); - pad = gst_pad_get_peer (tmppad); - - if (pad) { - /* Remove muxer request sink pad */ - gst_pad_unlink (tmppad, pad); - if (GST_PAD_TEMPLATE_PRESENCE (GST_PAD_PAD_TEMPLATE (pad)) == - GST_PAD_REQUEST) - gst_element_release_request_pad (ebin->muxer, pad); - gst_object_unref (pad); - } - gst_object_unref (tmppad); - } - gst_element_set_state (sgroup->outqueue, GST_STATE_NULL); - } - - if (sgroup->formatter) { - /* capsfilter - formatter - outqueue */ - gst_element_set_state (sgroup->formatter, GST_STATE_NULL); - gst_element_set_state (sgroup->outfilter, GST_STATE_NULL); - gst_element_unlink (sgroup->formatter, sgroup->outqueue); - gst_element_unlink (sgroup->outfilter, sgroup->formatter); - } else if (sgroup->outfilter) { - /* Capsfilter - outqueue */ - gst_element_set_state (sgroup->outfilter, GST_STATE_NULL); - gst_element_unlink (sgroup->outfilter, sgroup->outqueue); - } - - if (sgroup->outqueue) { - gst_element_set_state (sgroup->outqueue, GST_STATE_NULL); - gst_bin_remove (GST_BIN (ebin), sgroup->outqueue); - } - - /* streamcombiner - parser - capsfilter */ - if (sgroup->parser) { - gst_element_set_state (sgroup->parser, GST_STATE_NULL); - gst_element_unlink (sgroup->parser, sgroup->outfilter); - gst_element_unlink (sgroup->combiner, sgroup->parser); - gst_bin_remove ((GstBin *) ebin, sgroup->parser); - } - - /* Sink Ghostpad */ - if (sgroup->ghostpad) { - if (GST_PAD_PARENT (sgroup->ghostpad) != NULL) - gst_element_remove_pad (GST_ELEMENT_CAST (ebin), sgroup->ghostpad); - else - gst_object_unref (sgroup->ghostpad); - } - - if (sgroup->inqueue) - gst_element_set_state (sgroup->inqueue, GST_STATE_NULL); - - if (sgroup->encoder) - gst_element_set_state (sgroup->encoder, GST_STATE_NULL); - if (sgroup->fakesink) - gst_element_set_state (sgroup->fakesink, GST_STATE_NULL); - if (sgroup->outfilter) { - gst_element_set_state (sgroup->outfilter, GST_STATE_NULL); - - if (sgroup->outputfilter_caps_sid) { - g_signal_handler_disconnect (sgroup->outfilter->sinkpads->data, - sgroup->outputfilter_caps_sid); - sgroup->outputfilter_caps_sid = 0; - } - } - if (sgroup->smartencoder) - gst_element_set_state (sgroup->smartencoder, GST_STATE_NULL); - - if (sgroup->capsfilter) { - gst_element_set_state (sgroup->capsfilter, GST_STATE_NULL); - if (sgroup->encoder) - gst_element_unlink (sgroup->capsfilter, sgroup->encoder); - else - gst_element_unlink (sgroup->capsfilter, sgroup->fakesink); - - if (sgroup->inputfilter_caps_sid) { - g_signal_handler_disconnect (sgroup->capsfilter->sinkpads->data, - sgroup->inputfilter_caps_sid); - sgroup->inputfilter_caps_sid = 0; - } - gst_bin_remove ((GstBin *) ebin, sgroup->capsfilter); - } - - for (tmp = sgroup->converters; tmp; tmp = tmp->next) { - GstElement *elt = (GstElement *) tmp->data; - - gst_element_set_state (elt, GST_STATE_NULL); - gst_bin_remove ((GstBin *) ebin, elt); - } - if (sgroup->converters) - g_list_free (sgroup->converters); - - if (sgroup->combiner) { - GstIterator *it = gst_element_iterate_sink_pads (sgroup->combiner); - GstIteratorResult itret = GST_ITERATOR_OK; - - while (itret == GST_ITERATOR_OK || itret == GST_ITERATOR_RESYNC) { - itret = - gst_iterator_foreach (it, (GstIteratorForeachFunction) release_pads, - sgroup->combiner); - gst_iterator_resync (it); - } - gst_iterator_free (it); - gst_element_set_state (sgroup->combiner, GST_STATE_NULL); - gst_bin_remove ((GstBin *) ebin, sgroup->combiner); - } - - if (sgroup->splitter) { - GstIterator *it = gst_element_iterate_src_pads (sgroup->splitter); - GstIteratorResult itret = GST_ITERATOR_OK; - while (itret == GST_ITERATOR_OK || itret == GST_ITERATOR_RESYNC) { - itret = - gst_iterator_foreach (it, (GstIteratorForeachFunction) release_pads, - sgroup->splitter); - gst_iterator_resync (it); - } - gst_iterator_free (it); - - gst_element_set_state (sgroup->splitter, GST_STATE_NULL); - gst_bin_remove ((GstBin *) ebin, sgroup->splitter); - } - - if (sgroup->inqueue) - gst_bin_remove ((GstBin *) ebin, sgroup->inqueue); - - if (sgroup->encoder) - gst_bin_remove ((GstBin *) ebin, sgroup->encoder); - - if (sgroup->fakesink) - gst_bin_remove ((GstBin *) ebin, sgroup->fakesink); - - if (sgroup->smartencoder) - gst_bin_remove ((GstBin *) ebin, sgroup->smartencoder); - - if (sgroup->outfilter) - gst_bin_remove ((GstBin *) ebin, sgroup->outfilter); - - g_slice_free (StreamGroup, sgroup); -} - -static void -stream_group_remove (GstEncodeBin * ebin, StreamGroup * sgroup) -{ - ebin->streams = g_list_remove (ebin->streams, sgroup); - - stream_group_free (ebin, sgroup); -} - -static void -gst_encode_bin_tear_down_profile (GstEncodeBin * ebin) -{ - if (G_UNLIKELY (ebin->profile == NULL)) - return; - - GST_DEBUG ("Tearing down profile %s", - gst_encoding_profile_get_name (ebin->profile)); - - while (ebin->streams) - stream_group_remove (ebin, (StreamGroup *) ebin->streams->data); - - /* Set ghostpad target to NULL */ - gst_ghost_pad_set_target (GST_GHOST_PAD (ebin->srcpad), NULL); - - /* Remove muxer if present */ - if (ebin->muxer) { - gst_element_set_state (ebin->muxer, GST_STATE_NULL); - gst_bin_remove (GST_BIN (ebin), ebin->muxer); - ebin->muxer = NULL; - } - - /* free/clear profile */ - gst_encoding_profile_unref (ebin->profile); - ebin->profile = NULL; -} - -static gboolean -gst_encode_bin_setup_profile (GstEncodeBin * ebin, GstEncodingProfile * profile) -{ - gboolean res; - - g_return_val_if_fail (ebin->profile == NULL, FALSE); - - GST_DEBUG ("Setting up profile %p:%s (type:%s)", profile, - gst_encoding_profile_get_name (profile), - gst_encoding_profile_get_type_nick (profile)); - - ebin->profile = profile; - gst_object_ref (ebin->profile); - - /* Create elements */ - res = create_elements_and_pads (ebin); - if (!res) - gst_encode_bin_tear_down_profile (ebin); - - return res; -} - -static gboolean -gst_encode_bin_set_profile (GstEncodeBin * ebin, GstEncodingProfile * profile) -{ - g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); - - GST_DEBUG_OBJECT (ebin, "profile (%p) : %s", profile, - gst_encoding_profile_get_name (profile)); - - if (G_UNLIKELY (ebin->active)) { - GST_WARNING_OBJECT (ebin, "Element already active, can't change profile"); - return FALSE; - } - - /* If we're not active, we can deactivate the previous profile */ - if (ebin->profile) { - gst_encode_bin_tear_down_profile (ebin); - } - - return gst_encode_bin_setup_profile (ebin, profile); -} - -static inline gboolean -gst_encode_bin_activate (GstEncodeBin * ebin) -{ - ebin->active = ebin->profile != NULL; - return ebin->active; -} - -static void -gst_encode_bin_deactivate (GstEncodeBin * ebin) -{ - GList *tmp; - - for (tmp = ebin->streams; tmp; tmp = tmp->next) { - StreamGroup *sgroup = tmp->data; - GstCaps *format = gst_encoding_profile_get_format (sgroup->profile); - - _set_group_caps_format (sgroup, sgroup->profile, format); - - if (format) - gst_caps_unref (format); - } - - ebin->active = FALSE; -} - -static GstStateChangeReturn -gst_encode_bin_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn ret; - GstEncodeBin *ebin = (GstEncodeBin *) element; - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - if (!gst_encode_bin_activate (ebin)) { - ret = GST_STATE_CHANGE_FAILURE; - goto beach; - } - break; - default: - break; - } - - ret = - GST_ELEMENT_CLASS (gst_encode_bin_parent_class)->change_state (element, - transition); - if (ret == GST_STATE_CHANGE_FAILURE) - goto beach; - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_encode_bin_deactivate (ebin); - break; - default: - break; - } - -beach: - return ret; -} - - -static gboolean -plugin_init (GstPlugin * plugin) -{ - gboolean res; - - GST_DEBUG_CATEGORY_INIT (gst_encode_bin_debug, "encodebin", 0, "encoder bin"); - -#ifdef ENABLE_NLS - GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE, - LOCALEDIR); - bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); -#endif /* ENABLE_NLS */ - - - res = gst_element_register (plugin, "encodebin", GST_RANK_NONE, - GST_TYPE_ENCODE_BIN); - - return res; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - encoding, - "various encoding-related elements", plugin_init, VERSION, GST_LICENSE, - GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/gst/encoding/gstencodebin.h b/gst/encoding/gstencodebin.h deleted file mode 100644 index 021e690..0000000 --- a/gst/encoding/gstencodebin.h +++ /dev/null @@ -1,38 +0,0 @@ -/* GStreamer encoding bin - * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk> - * (C) 2009 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_ENCODEBIN_H__ -#define __GST_ENCODEBIN_H__ - -#include <gst/gst.h> -#include <gst/pbutils/pbutils.h> - -#define GST_TYPE_ENCODE_BIN (gst_encode_bin_get_type()) -#define GST_ENCODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ENCODE_BIN,GstEncodeBin)) -#define GST_ENCODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ENCODE_BIN,GstEncodeBinClass)) -#define GST_IS_ENCODE_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ENCODE_BIN)) -#define GST_IS_ENCODE_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ENCODE_BIN)) - -typedef struct _GstEncodeBin GstEncodeBin; -typedef struct _GstEncodeBinClass GstEncodeBinClass; - -GType gst_encode_bin_get_type(void); - -#endif /* __GST_ENCODEBIN_H__ */