| /* GStreamer Smart Video Encoder element |
| * Copyright (C) <2010> Edward Hervey <bilboed@gmail.com> |
| * |
| * 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. |
| */ |
| |
| /* TODO: |
| * * Implement get_caps/set_caps (store/forward caps) |
| * * Adjust template caps to the formats we can support |
| **/ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <string.h> |
| #include "gstsmartencoder.h" |
| |
| GST_DEBUG_CATEGORY_STATIC (smart_encoder_debug); |
| #define GST_CAT_DEFAULT smart_encoder_debug |
| |
| /* FIXME : Update this with new caps */ |
| /* WARNING : We can only allow formats with closed-GOP */ |
| #define ALLOWED_CAPS "video/x-h263;video/x-intel-h263;"\ |
| "video/mpeg,mpegversion=(int)1,systemstream=(boolean)false;"\ |
| "video/mpeg,mpegversion=(int)2,systemstream=(boolean)false;" |
| |
| static GstStaticPadTemplate src_template = |
| GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS (ALLOWED_CAPS) |
| ); |
| |
| static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", |
| GST_PAD_SINK, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS (ALLOWED_CAPS) |
| ); |
| |
| static GQuark INTERNAL_ELEMENT; |
| |
| /* GstSmartEncoder signals and args */ |
| enum |
| { |
| /* FILL ME */ |
| LAST_SIGNAL |
| }; |
| |
| enum |
| { |
| PROP_0 |
| /* FILL ME */ |
| }; |
| |
| static void |
| _do_init (void) |
| { |
| INTERNAL_ELEMENT = g_quark_from_static_string ("internal-element"); |
| }; |
| |
| G_DEFINE_TYPE_EXTENDED (GstSmartEncoder, gst_smart_encoder, GST_TYPE_ELEMENT, 0, |
| _do_init ()); |
| |
| static void gst_smart_encoder_dispose (GObject * object); |
| |
| static gboolean setup_recoder_pipeline (GstSmartEncoder * smart_encoder); |
| |
| static GstFlowReturn gst_smart_encoder_chain (GstPad * pad, GstObject * parent, |
| GstBuffer * buf); |
| static gboolean smart_encoder_sink_event (GstPad * pad, GstObject * parent, |
| GstEvent * event); |
| static gboolean smart_encoder_sink_query (GstPad * pad, GstObject * parent, |
| GstQuery * query); |
| static GstCaps *smart_encoder_sink_getcaps (GstPad * pad, GstCaps * filter); |
| static GstStateChangeReturn |
| gst_smart_encoder_change_state (GstElement * element, |
| GstStateChange transition); |
| |
| static void |
| gst_smart_encoder_class_init (GstSmartEncoderClass * klass) |
| { |
| GObjectClass *gobject_class; |
| GstElementClass *element_class; |
| |
| element_class = (GstElementClass *) klass; |
| gobject_class = G_OBJECT_CLASS (klass); |
| |
| gst_smart_encoder_parent_class = g_type_class_peek_parent (klass); |
| |
| gst_element_class_add_static_pad_template (element_class, &src_template); |
| gst_element_class_add_static_pad_template (element_class, &sink_template); |
| |
| gst_element_class_set_static_metadata (element_class, "Smart Video Encoder", |
| "Codec/Recoder/Video", |
| "Re-encodes portions of Video that lay on segment boundaries", |
| "Edward Hervey <bilboed@gmail.com>"); |
| |
| gobject_class->dispose = (GObjectFinalizeFunc) (gst_smart_encoder_dispose); |
| element_class->change_state = gst_smart_encoder_change_state; |
| |
| GST_DEBUG_CATEGORY_INIT (smart_encoder_debug, "smartencoder", 0, |
| "Smart Encoder"); |
| } |
| |
| static void |
| smart_encoder_reset (GstSmartEncoder * smart_encoder) |
| { |
| gst_segment_init (smart_encoder->segment, GST_FORMAT_UNDEFINED); |
| |
| if (smart_encoder->encoder) { |
| /* Clean up/remove elements */ |
| gst_element_set_state (smart_encoder->encoder, GST_STATE_NULL); |
| gst_element_set_state (smart_encoder->decoder, GST_STATE_NULL); |
| gst_element_set_bus (smart_encoder->encoder, NULL); |
| gst_element_set_bus (smart_encoder->decoder, NULL); |
| gst_pad_set_active (smart_encoder->internal_srcpad, FALSE); |
| gst_pad_set_active (smart_encoder->internal_sinkpad, FALSE); |
| gst_object_unref (smart_encoder->encoder); |
| gst_object_unref (smart_encoder->decoder); |
| gst_object_unref (smart_encoder->internal_srcpad); |
| gst_object_unref (smart_encoder->internal_sinkpad); |
| |
| smart_encoder->encoder = NULL; |
| smart_encoder->decoder = NULL; |
| smart_encoder->internal_sinkpad = NULL; |
| smart_encoder->internal_srcpad = NULL; |
| } |
| |
| if (smart_encoder->newsegment) { |
| gst_event_unref (smart_encoder->newsegment); |
| smart_encoder->newsegment = NULL; |
| } |
| } |
| |
| |
| static void |
| gst_smart_encoder_init (GstSmartEncoder * smart_encoder) |
| { |
| smart_encoder->sinkpad = |
| gst_pad_new_from_static_template (&sink_template, "sink"); |
| gst_pad_set_chain_function (smart_encoder->sinkpad, gst_smart_encoder_chain); |
| gst_pad_set_event_function (smart_encoder->sinkpad, smart_encoder_sink_event); |
| gst_pad_set_query_function (smart_encoder->sinkpad, smart_encoder_sink_query); |
| gst_element_add_pad (GST_ELEMENT (smart_encoder), smart_encoder->sinkpad); |
| |
| smart_encoder->srcpad = |
| gst_pad_new_from_static_template (&src_template, "src"); |
| gst_pad_use_fixed_caps (smart_encoder->srcpad); |
| gst_element_add_pad (GST_ELEMENT (smart_encoder), smart_encoder->srcpad); |
| |
| smart_encoder->segment = gst_segment_new (); |
| |
| smart_encoder_reset (smart_encoder); |
| } |
| |
| void |
| gst_smart_encoder_dispose (GObject * object) |
| { |
| GstSmartEncoder *smart_encoder = (GstSmartEncoder *) object; |
| |
| if (smart_encoder->segment) |
| gst_segment_free (smart_encoder->segment); |
| smart_encoder->segment = NULL; |
| if (smart_encoder->available_caps) |
| gst_caps_unref (smart_encoder->available_caps); |
| smart_encoder->available_caps = NULL; |
| G_OBJECT_CLASS (gst_smart_encoder_parent_class)->dispose (object); |
| } |
| |
| static GstFlowReturn |
| gst_smart_encoder_reencode_gop (GstSmartEncoder * smart_encoder) |
| { |
| GstFlowReturn res = GST_FLOW_OK; |
| GList *tmp; |
| |
| if (smart_encoder->encoder == NULL) { |
| if (!setup_recoder_pipeline (smart_encoder)) |
| return GST_FLOW_ERROR; |
| } |
| |
| /* Activate elements */ |
| /* Set elements to PAUSED */ |
| gst_element_set_state (smart_encoder->encoder, GST_STATE_PAUSED); |
| gst_element_set_state (smart_encoder->decoder, GST_STATE_PAUSED); |
| |
| GST_INFO ("Pushing Flush start/stop to clean decoder/encoder"); |
| gst_pad_push_event (smart_encoder->internal_srcpad, |
| gst_event_new_flush_start ()); |
| gst_pad_push_event (smart_encoder->internal_srcpad, |
| gst_event_new_flush_stop (TRUE)); |
| |
| /* push newsegment */ |
| GST_INFO ("Pushing newsegment %" GST_PTR_FORMAT, smart_encoder->newsegment); |
| gst_pad_push_event (smart_encoder->internal_srcpad, |
| gst_event_ref (smart_encoder->newsegment)); |
| |
| /* Push buffers through our pads */ |
| GST_DEBUG ("Pushing pending buffers"); |
| |
| for (tmp = smart_encoder->pending_gop; tmp; tmp = tmp->next) { |
| GstBuffer *buf = (GstBuffer *) tmp->data; |
| |
| res = gst_pad_push (smart_encoder->internal_srcpad, buf); |
| if (G_UNLIKELY (res != GST_FLOW_OK)) |
| break; |
| } |
| |
| if (G_UNLIKELY (res != GST_FLOW_OK)) { |
| GST_WARNING ("Error pushing pending buffers : %s", gst_flow_get_name (res)); |
| /* Remove pending bfufers */ |
| for (tmp = smart_encoder->pending_gop; tmp; tmp = tmp->next) { |
| gst_buffer_unref ((GstBuffer *) tmp->data); |
| } |
| } else { |
| GST_INFO ("Pushing out EOS to flush out decoder/encoder"); |
| gst_pad_push_event (smart_encoder->internal_srcpad, gst_event_new_eos ()); |
| } |
| |
| /* Activate elements */ |
| /* Set elements to PAUSED */ |
| gst_element_set_state (smart_encoder->encoder, GST_STATE_NULL); |
| gst_element_set_state (smart_encoder->decoder, GST_STATE_NULL); |
| |
| g_list_free (smart_encoder->pending_gop); |
| smart_encoder->pending_gop = NULL; |
| |
| return res; |
| } |
| |
| static GstFlowReturn |
| gst_smart_encoder_push_pending_gop (GstSmartEncoder * smart_encoder) |
| { |
| guint64 cstart, cstop; |
| GList *tmp; |
| GstFlowReturn res = GST_FLOW_OK; |
| |
| GST_DEBUG ("Pushing pending GOP (%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT |
| ")", GST_TIME_ARGS (smart_encoder->gop_start), |
| GST_TIME_ARGS (smart_encoder->gop_stop)); |
| |
| /* If GOP is entirely within segment, just push downstream */ |
| if (gst_segment_clip (smart_encoder->segment, GST_FORMAT_TIME, |
| smart_encoder->gop_start, smart_encoder->gop_stop, &cstart, &cstop)) { |
| if ((cstart != smart_encoder->gop_start) |
| || (cstop != smart_encoder->gop_stop)) { |
| GST_DEBUG ("GOP needs to be re-encoded from %" GST_TIME_FORMAT " to %" |
| GST_TIME_FORMAT, GST_TIME_ARGS (cstart), GST_TIME_ARGS (cstop)); |
| res = gst_smart_encoder_reencode_gop (smart_encoder); |
| } else { |
| /* The whole GOP is within the segment, push all pending buffers downstream */ |
| GST_DEBUG ("GOP doesn't need to be modified, pushing downstream"); |
| for (tmp = smart_encoder->pending_gop; tmp; tmp = tmp->next) { |
| GstBuffer *buf = (GstBuffer *) tmp->data; |
| res = gst_pad_push (smart_encoder->srcpad, buf); |
| if (G_UNLIKELY (res != GST_FLOW_OK)) |
| break; |
| } |
| } |
| } else { |
| /* The whole GOP is outside the segment, there's most likely |
| * a bug somewhere. */ |
| GST_WARNING |
| ("GOP is entirely outside of the segment, upstream gave us too much data"); |
| for (tmp = smart_encoder->pending_gop; tmp; tmp = tmp->next) { |
| gst_buffer_unref ((GstBuffer *) tmp->data); |
| } |
| } |
| |
| if (smart_encoder->pending_gop) { |
| g_list_free (smart_encoder->pending_gop); |
| smart_encoder->pending_gop = NULL; |
| } |
| smart_encoder->gop_start = GST_CLOCK_TIME_NONE; |
| smart_encoder->gop_stop = GST_CLOCK_TIME_NONE; |
| |
| return res; |
| } |
| |
| static GstFlowReturn |
| gst_smart_encoder_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) |
| { |
| GstSmartEncoder *smart_encoder; |
| GstFlowReturn res = GST_FLOW_OK; |
| gboolean discont, keyframe; |
| |
| smart_encoder = GST_SMART_ENCODER (parent); |
| |
| discont = GST_BUFFER_IS_DISCONT (buf); |
| keyframe = !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); |
| |
| GST_DEBUG ("New buffer %s %s %" GST_TIME_FORMAT, |
| discont ? "discont" : "", |
| keyframe ? "keyframe" : "", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); |
| |
| if (keyframe) { |
| GST_DEBUG ("Got a keyframe"); |
| |
| /* If there's a pending GOP, flush it out */ |
| if (smart_encoder->pending_gop) { |
| /* Mark gop_stop */ |
| smart_encoder->gop_stop = GST_BUFFER_TIMESTAMP (buf); |
| |
| /* flush pending */ |
| res = gst_smart_encoder_push_pending_gop (smart_encoder); |
| if (G_UNLIKELY (res != GST_FLOW_OK)) |
| goto beach; |
| } |
| |
| /* Mark gop_start for new gop */ |
| smart_encoder->gop_start = GST_BUFFER_TIMESTAMP (buf); |
| } |
| |
| /* Store buffer */ |
| smart_encoder->pending_gop = g_list_append (smart_encoder->pending_gop, buf); |
| /* Update GOP stop position */ |
| if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { |
| smart_encoder->gop_stop = GST_BUFFER_TIMESTAMP (buf); |
| if (GST_BUFFER_DURATION_IS_VALID (buf)) |
| smart_encoder->gop_stop += GST_BUFFER_DURATION (buf); |
| } |
| |
| GST_DEBUG ("Buffer stored , Current GOP : %" GST_TIME_FORMAT " -- %" |
| GST_TIME_FORMAT, GST_TIME_ARGS (smart_encoder->gop_start), |
| GST_TIME_ARGS (smart_encoder->gop_stop)); |
| |
| beach: |
| return res; |
| } |
| |
| static gboolean |
| smart_encoder_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) |
| { |
| gboolean res = TRUE; |
| GstSmartEncoder *smart_encoder = GST_SMART_ENCODER (parent); |
| |
| switch (GST_EVENT_TYPE (event)) { |
| case GST_EVENT_FLUSH_STOP: |
| smart_encoder_reset (smart_encoder); |
| break; |
| case GST_EVENT_SEGMENT: |
| { |
| gst_event_copy_segment (event, smart_encoder->segment); |
| |
| GST_DEBUG_OBJECT (smart_encoder, "segment: %" GST_SEGMENT_FORMAT, |
| smart_encoder->segment); |
| if (smart_encoder->segment->format != GST_FORMAT_TIME) { |
| GST_ERROR |
| ("smart_encoder can not handle streams not specified in GST_FORMAT_TIME"); |
| gst_event_unref (event); |
| return FALSE; |
| } |
| |
| /* And keep a copy for further usage */ |
| if (smart_encoder->newsegment) |
| gst_event_unref (smart_encoder->newsegment); |
| smart_encoder->newsegment = gst_event_ref (event); |
| } |
| break; |
| case GST_EVENT_EOS: |
| GST_DEBUG ("Eos, flushing remaining data"); |
| if (smart_encoder->segment->format == GST_FORMAT_TIME) |
| gst_smart_encoder_push_pending_gop (smart_encoder); |
| break; |
| default: |
| break; |
| } |
| |
| res = gst_pad_push_event (smart_encoder->srcpad, event); |
| |
| return res; |
| } |
| |
| static GstCaps * |
| smart_encoder_sink_getcaps (GstPad * pad, GstCaps * filter) |
| { |
| GstCaps *peer, *tmpl, *res; |
| GstSmartEncoder *smart_encoder = GST_SMART_ENCODER (gst_pad_get_parent (pad)); |
| |
| /* Use computed caps */ |
| if (smart_encoder->available_caps) |
| tmpl = gst_caps_ref (smart_encoder->available_caps); |
| else |
| tmpl = gst_static_pad_template_get_caps (&src_template); |
| |
| /* Try getting it from downstream */ |
| peer = gst_pad_peer_query_caps (smart_encoder->srcpad, tmpl); |
| |
| if (peer == NULL) { |
| res = tmpl; |
| } else { |
| res = peer; |
| gst_caps_unref (tmpl); |
| } |
| |
| gst_object_unref (smart_encoder); |
| return res; |
| } |
| |
| static gboolean |
| smart_encoder_sink_query (GstPad * pad, GstObject * parent, GstQuery * query) |
| { |
| gboolean res; |
| |
| switch (GST_QUERY_TYPE (query)) { |
| case GST_QUERY_CAPS: |
| { |
| GstCaps *filter, *caps; |
| |
| gst_query_parse_caps (query, &filter); |
| caps = smart_encoder_sink_getcaps (pad, filter); |
| gst_query_set_caps_result (query, caps); |
| gst_caps_unref (caps); |
| res = TRUE; |
| break; |
| } |
| default: |
| res = gst_pad_query_default (pad, parent, query); |
| break; |
| } |
| return res; |
| } |
| |
| /***************************************** |
| * Internal encoder/decoder pipeline * |
| ******************************************/ |
| |
| static GstElementFactory * |
| get_decoder_factory (GstCaps * caps) |
| { |
| GstElementFactory *fact = NULL; |
| GList *decoders, *tmp; |
| |
| tmp = |
| gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_DECODER, |
| GST_RANK_MARGINAL); |
| decoders = gst_element_factory_list_filter (tmp, caps, GST_PAD_SINK, FALSE); |
| gst_plugin_feature_list_free (tmp); |
| |
| for (tmp = decoders; tmp; tmp = tmp->next) { |
| /* We just pick the first one */ |
| fact = (GstElementFactory *) tmp->data; |
| gst_object_ref (fact); |
| break; |
| } |
| |
| gst_plugin_feature_list_free (decoders); |
| |
| return fact; |
| } |
| |
| static GstElementFactory * |
| get_encoder_factory (GstCaps * caps) |
| { |
| GstElementFactory *fact = NULL; |
| GList *encoders, *tmp; |
| |
| tmp = |
| gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_ENCODER, |
| GST_RANK_MARGINAL); |
| encoders = gst_element_factory_list_filter (tmp, caps, GST_PAD_SRC, FALSE); |
| gst_plugin_feature_list_free (tmp); |
| |
| for (tmp = encoders; tmp; tmp = tmp->next) { |
| /* We just pick the first one */ |
| fact = (GstElementFactory *) tmp->data; |
| gst_object_ref (fact); |
| break; |
| } |
| |
| gst_plugin_feature_list_free (encoders); |
| |
| return fact; |
| } |
| |
| static GstElement * |
| get_decoder (GstCaps * caps) |
| { |
| GstElementFactory *fact = get_decoder_factory (caps); |
| GstElement *res = NULL; |
| |
| if (fact) { |
| res = gst_element_factory_create (fact, "internal-decoder"); |
| gst_object_unref (fact); |
| } |
| return res; |
| } |
| |
| static GstElement * |
| get_encoder (GstCaps * caps) |
| { |
| GstElementFactory *fact = get_encoder_factory (caps); |
| GstElement *res = NULL; |
| |
| if (fact) { |
| res = gst_element_factory_create (fact, "internal-encoder"); |
| gst_object_unref (fact); |
| } |
| return res; |
| } |
| |
| static GstFlowReturn |
| internal_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) |
| { |
| GstSmartEncoder *smart_encoder = |
| g_object_get_qdata ((GObject *) pad, INTERNAL_ELEMENT); |
| |
| return gst_pad_push (smart_encoder->srcpad, buf); |
| } |
| |
| static gboolean |
| setup_recoder_pipeline (GstSmartEncoder * smart_encoder) |
| { |
| GstPad *tmppad; |
| GstCaps *caps; |
| |
| /* Fast path */ |
| if (G_UNLIKELY (smart_encoder->encoder)) |
| return TRUE; |
| |
| GST_DEBUG ("Creating internal decoder and encoder"); |
| |
| /* Create decoder/encoder */ |
| caps = gst_pad_get_current_caps (smart_encoder->sinkpad); |
| smart_encoder->decoder = get_decoder (caps); |
| if (G_UNLIKELY (smart_encoder->decoder == NULL)) |
| goto no_decoder; |
| gst_caps_unref (caps); |
| gst_element_set_bus (smart_encoder->decoder, GST_ELEMENT_BUS (smart_encoder)); |
| |
| caps = gst_pad_get_current_caps (smart_encoder->sinkpad); |
| smart_encoder->encoder = get_encoder (caps); |
| if (G_UNLIKELY (smart_encoder->encoder == NULL)) |
| goto no_encoder; |
| gst_caps_unref (caps); |
| gst_element_set_bus (smart_encoder->encoder, GST_ELEMENT_BUS (smart_encoder)); |
| |
| GST_DEBUG ("Creating internal pads"); |
| |
| /* Create internal pads */ |
| |
| /* Source pad which we'll use to feed data to decoders */ |
| smart_encoder->internal_srcpad = gst_pad_new ("internal_src", GST_PAD_SRC); |
| g_object_set_qdata ((GObject *) smart_encoder->internal_srcpad, |
| INTERNAL_ELEMENT, smart_encoder); |
| gst_pad_set_active (smart_encoder->internal_srcpad, TRUE); |
| |
| /* Sink pad which will get the buffers from the encoder. |
| * Note: We don't need an event function since we'll be discarding all |
| * of them. */ |
| smart_encoder->internal_sinkpad = gst_pad_new ("internal_sink", GST_PAD_SINK); |
| g_object_set_qdata ((GObject *) smart_encoder->internal_sinkpad, |
| INTERNAL_ELEMENT, smart_encoder); |
| gst_pad_set_chain_function (smart_encoder->internal_sinkpad, internal_chain); |
| gst_pad_set_active (smart_encoder->internal_sinkpad, TRUE); |
| |
| GST_DEBUG ("Linking pads to elements"); |
| |
| /* Link everything */ |
| tmppad = gst_element_get_static_pad (smart_encoder->encoder, "src"); |
| if (GST_PAD_LINK_FAILED (gst_pad_link (tmppad, |
| smart_encoder->internal_sinkpad))) |
| goto sinkpad_link_fail; |
| gst_object_unref (tmppad); |
| |
| if (!gst_element_link (smart_encoder->decoder, smart_encoder->encoder)) |
| goto encoder_decoder_link_fail; |
| |
| tmppad = gst_element_get_static_pad (smart_encoder->decoder, "sink"); |
| if (GST_PAD_LINK_FAILED (gst_pad_link (smart_encoder->internal_srcpad, |
| tmppad))) |
| goto srcpad_link_fail; |
| gst_object_unref (tmppad); |
| |
| GST_DEBUG ("Done creating internal elements/pads"); |
| |
| return TRUE; |
| |
| no_decoder: |
| { |
| GST_WARNING ("Couldn't find a decoder for %" GST_PTR_FORMAT, caps); |
| gst_caps_unref (caps); |
| return FALSE; |
| } |
| |
| no_encoder: |
| { |
| GST_WARNING ("Couldn't find an encoder for %" GST_PTR_FORMAT, caps); |
| gst_caps_unref (caps); |
| return FALSE; |
| } |
| |
| srcpad_link_fail: |
| { |
| gst_object_unref (tmppad); |
| GST_WARNING ("Couldn't link internal srcpad to decoder"); |
| return FALSE; |
| } |
| |
| sinkpad_link_fail: |
| { |
| gst_object_unref (tmppad); |
| GST_WARNING ("Couldn't link encoder to internal sinkpad"); |
| return FALSE; |
| } |
| |
| encoder_decoder_link_fail: |
| { |
| GST_WARNING ("Couldn't link decoder to encoder"); |
| return FALSE; |
| } |
| } |
| |
| static GstStateChangeReturn |
| gst_smart_encoder_find_elements (GstSmartEncoder * smart_encoder) |
| { |
| guint i, n; |
| GstCaps *tmpl, *st, *res; |
| GstElementFactory *dec, *enc; |
| GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; |
| |
| if (G_UNLIKELY (smart_encoder->available_caps)) |
| goto beach; |
| |
| /* Iterate over all pad template caps and see if we have both an |
| * encoder and a decoder for those media types */ |
| tmpl = gst_static_pad_template_get_caps (&src_template); |
| res = gst_caps_new_empty (); |
| n = gst_caps_get_size (tmpl); |
| |
| for (i = 0; i < n; i++) { |
| st = gst_caps_copy_nth (tmpl, i); |
| GST_DEBUG_OBJECT (smart_encoder, |
| "Checking for available decoder and encoder for %" GST_PTR_FORMAT, st); |
| if (!(dec = get_decoder_factory (st))) { |
| gst_caps_unref (st); |
| continue; |
| } |
| gst_object_unref (dec); |
| if (!(enc = get_encoder_factory (st))) { |
| gst_caps_unref (st); |
| continue; |
| } |
| gst_object_unref (enc); |
| GST_DEBUG_OBJECT (smart_encoder, "OK"); |
| gst_caps_append (res, st); |
| } |
| |
| gst_caps_unref (tmpl); |
| |
| if (gst_caps_is_empty (res)) { |
| gst_caps_unref (res); |
| ret = GST_STATE_CHANGE_FAILURE; |
| } else |
| smart_encoder->available_caps = res; |
| |
| GST_DEBUG_OBJECT (smart_encoder, "Done, available_caps:%" GST_PTR_FORMAT, |
| smart_encoder->available_caps); |
| |
| beach: |
| return ret; |
| } |
| |
| /****************************************** |
| * GstElement vmethod implementations * |
| ******************************************/ |
| |
| static GstStateChangeReturn |
| gst_smart_encoder_change_state (GstElement * element, GstStateChange transition) |
| { |
| GstSmartEncoder *smart_encoder; |
| GstStateChangeReturn ret; |
| |
| g_return_val_if_fail (GST_IS_SMART_ENCODER (element), |
| GST_STATE_CHANGE_FAILURE); |
| |
| smart_encoder = GST_SMART_ENCODER (element); |
| |
| switch (transition) { |
| case GST_STATE_CHANGE_NULL_TO_READY: |
| /* Figure out which elements are available */ |
| if ((ret = |
| gst_smart_encoder_find_elements (smart_encoder)) == |
| GST_STATE_CHANGE_FAILURE) |
| goto beach; |
| break; |
| default: |
| break; |
| } |
| |
| ret = |
| GST_ELEMENT_CLASS (gst_smart_encoder_parent_class)->change_state (element, |
| transition); |
| |
| switch (transition) { |
| case GST_STATE_CHANGE_PAUSED_TO_READY: |
| smart_encoder_reset (smart_encoder); |
| break; |
| default: |
| break; |
| } |
| |
| beach: |
| return ret; |
| } |