mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-12 03:16:33 +00:00
7ba7c58f99
... and don't try to push pending data without ever having received a SEGMENT event before EOS https://bugzilla.gnome.org/show_bug.cgi?id=765541
717 lines
21 KiB
C
717 lines
21 KiB
C
/* 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;
|
|
}
|