diff --git a/configure.ac b/configure.ac index 01b907c8a4..239356236e 100644 --- a/configure.ac +++ b/configure.ac @@ -296,6 +296,7 @@ AG_GST_CHECK_PLUGIN(real) AG_GST_CHECK_PLUGIN(rtpmux) AG_GST_CHECK_PLUGIN(scaletempo) AG_GST_CHECK_PLUGIN(sdp) +AG_GST_CHECK_PLUGIN(segmentclip) AG_GST_CHECK_PLUGIN(selector) AG_GST_CHECK_PLUGIN(siren) AG_GST_CHECK_PLUGIN(speed) @@ -1667,6 +1668,7 @@ gst/real/Makefile gst/rtpmux/Makefile gst/scaletempo/Makefile gst/sdp/Makefile +gst/segmentclip/Makefile gst/selector/Makefile gst/siren/Makefile gst/speed/Makefile diff --git a/gst/segmentclip/Makefile.am b/gst/segmentclip/Makefile.am new file mode 100644 index 0000000000..f87feee0ea --- /dev/null +++ b/gst/segmentclip/Makefile.am @@ -0,0 +1,11 @@ +plugin_LTLIBRARIES = libgstsegmentclip.la + +libgstsegmentclip_la_SOURCES = gstsegmentclip.c gstaudiosegmentclip.c gstvideosegmentclip.c plugin.c + +libgstsegmentclip_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) +libgstsegmentclip_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) $(GST_LIBS) -lgstaudio-@GST_MAJORMINOR@ +libgstsegmentclip_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstsegmentclip_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = gstsegmentclip.h gstaudiosegmentclip.h gstvideosegmentclip.h + diff --git a/gst/segmentclip/gstaudiosegmentclip.c b/gst/segmentclip/gstaudiosegmentclip.c new file mode 100644 index 0000000000..93602c5d28 --- /dev/null +++ b/gst/segmentclip/gstaudiosegmentclip.c @@ -0,0 +1,191 @@ +/* GStreamer + * Copyright (C) 2010 Sebastian Dröge + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "gstaudiosegmentclip.h" + +static GstStaticPadTemplate sink_pad_template = + GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS + ("audio/x-raw-int, width=(int)[1,MAX], channels=(int)[1,MAX],rate=(int)[1,MAX]; audio/x-raw-float, width=(int)[1,MAX], channels=(int)[1,MAX],rate=(int)[1,MAX]")); + +static GstStaticPadTemplate src_pad_template = + GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS + ("audio/x-raw-int, width=(int)[1,MAX], channels=(int)[1,MAX],rate=(int)[1,MAX]; audio/x-raw-float, width=(int)[1,MAX], channels=(int)[1,MAX],rate=(int)[1,MAX]")); + +static void gst_audio_segment_clip_reset (GstSegmentClip * self); +static GstFlowReturn gst_audio_segment_clip_clip_buffer (GstSegmentClip * self, + GstBuffer * buffer, GstBuffer ** outbuf); +static gboolean gst_audio_segment_clip_set_caps (GstSegmentClip * self, + GstCaps * caps); + +GST_DEBUG_CATEGORY_STATIC (gst_audio_segment_clip_debug); +#define GST_CAT_DEFAULT gst_audio_segment_clip_debug + +GST_BOILERPLATE (GstAudioSegmentClip, gst_audio_segment_clip, GstSegmentClip, + GST_TYPE_SEGMENT_CLIP); + +static void +gst_audio_segment_clip_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details_simple (element_class, + "Audio buffer segment clipper", + "Filter/Audio", + "Clips audio buffers to the configured segment", + "Sebastian Dröge "); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_pad_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_pad_template)); +} + +static void +gst_audio_segment_clip_class_init (GstAudioSegmentClipClass * klass) +{ + GstSegmentClipClass *segment_clip_klass = GST_SEGMENT_CLIP_CLASS (klass); + + GST_DEBUG_CATEGORY_INIT (gst_audio_segment_clip_debug, "audiosegmentclip", 0, + "audiosegmentclip element"); + + segment_clip_klass->reset = GST_DEBUG_FUNCPTR (gst_audio_segment_clip_reset); + segment_clip_klass->set_caps = + GST_DEBUG_FUNCPTR (gst_audio_segment_clip_set_caps); + segment_clip_klass->clip_buffer = + GST_DEBUG_FUNCPTR (gst_audio_segment_clip_clip_buffer); +} + +static void +gst_audio_segment_clip_init (GstAudioSegmentClip * self, + GstAudioSegmentClipClass * g_class) +{ +} + +static void +gst_audio_segment_clip_reset (GstSegmentClip * base) +{ + GstAudioSegmentClip *self = GST_AUDIO_SEGMENT_CLIP (base); + + GST_DEBUG_OBJECT (self, "Resetting internal state"); + + self->rate = self->framesize = 0; +} + + +static GstFlowReturn +gst_audio_segment_clip_clip_buffer (GstSegmentClip * base, GstBuffer * buffer, + GstBuffer ** outbuf) +{ + GstAudioSegmentClip *self = GST_AUDIO_SEGMENT_CLIP (base); + GstSegment *segment = &base->segment; + GstClockTime timestamp = GST_BUFFER_TIMESTAMP (buffer); + GstClockTime duration = GST_BUFFER_DURATION (buffer); + guint64 offset = GST_BUFFER_OFFSET (buffer); + guint64 offset_end = GST_BUFFER_OFFSET_END (buffer); + guint size = GST_BUFFER_SIZE (buffer); + + if (!self->rate || !self->framesize) { + GST_ERROR_OBJECT (self, "Not negotiated yet"); + gst_buffer_unref (buffer); + return GST_FLOW_NOT_NEGOTIATED; + } + + if (segment->format != GST_FORMAT_DEFAULT && + segment->format != GST_FORMAT_TIME) { + GST_DEBUG_OBJECT (self, "Unsupported segment format %s", + gst_format_get_name (segment->format)); + *outbuf = buffer; + return GST_FLOW_OK; + } + + if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) { + GST_WARNING_OBJECT (self, "Buffer without valid timestamp"); + *outbuf = buffer; + return GST_FLOW_OK; + } + + *outbuf = + gst_audio_buffer_clip (buffer, segment, self->rate, self->framesize); + + if (!*outbuf) { + GST_DEBUG_OBJECT (self, "Buffer outside the configured segment"); + + /* Now return unexpected if we're before/after the end */ + if (segment->format == GST_FORMAT_TIME) { + if (segment->rate >= 0) { + if (segment->stop != -1 && timestamp >= segment->stop) + return GST_FLOW_UNEXPECTED; + } else { + if (!GST_CLOCK_TIME_IS_VALID (duration)) + duration = + gst_util_uint64_scale_int (size, GST_SECOND, + self->framesize * self->rate); + + if (segment->start != -1 && timestamp + duration <= segment->start) + return GST_FLOW_UNEXPECTED; + } + } else { + if (segment->rate >= 0) { + if (segment->stop != -1 && offset != -1 && offset >= segment->stop) + return GST_FLOW_UNEXPECTED; + } else if (offset != -1 || offset_end != -1) { + if (offset_end == -1) + offset_end = offset + size / self->framesize; + + if (segment->start != -1 && offset_end <= segment->start) + return GST_FLOW_UNEXPECTED; + } + } + } + + return GST_FLOW_OK; +} + +static gboolean +gst_audio_segment_clip_set_caps (GstSegmentClip * base, GstCaps * caps) +{ + GstAudioSegmentClip *self = GST_AUDIO_SEGMENT_CLIP (base); + gboolean ret; + GstStructure *s; + gint rate, channels, width; + + s = gst_caps_get_structure (caps, 0); + + ret = gst_structure_get_int (s, "rate", &rate); + ret = ret && gst_structure_get_int (s, "channels", &channels); + ret = ret && gst_structure_get_int (s, "width", &width); + + if (ret) { + GST_DEBUG_OBJECT (self, "Configured: rate %d channels %d width %d", rate, + channels, width); + self->rate = rate; + self->framesize = (width / 8) * channels; + } + + return ret; +} diff --git a/gst/segmentclip/gstaudiosegmentclip.h b/gst/segmentclip/gstaudiosegmentclip.h new file mode 100644 index 0000000000..45c615ad5a --- /dev/null +++ b/gst/segmentclip/gstaudiosegmentclip.h @@ -0,0 +1,61 @@ +/* GStreamer + * Copyright (C) 2010 Sebastian Dröge + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_AUDIO_SEGMENT_CLIP_H__ +#define __GST_AUDIO_SEGMENT_CLIP_H__ + +#include +#include "gstsegmentclip.h" + +G_BEGIN_DECLS + +#define GST_TYPE_AUDIO_SEGMENT_CLIP \ + (gst_audio_segment_clip_get_type()) +#define GST_AUDIO_SEGMENT_CLIP(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_SEGMENT_CLIP,GstAudioSegmentClip)) +#define GST_AUDIO_SEGMENT_CLIP_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIO_SEGMENT_CLIP,GstAudioSegmentClipClass)) +#define GST_AUDIO_SEGMENT_CLIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_AUDIO_SEGMENT_CLIP,GstAudioSegmentClipClass)) +#define GST_IS_AUDIO_SEGMENT_CLIP(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_SEGMENT_CLIP)) +#define GST_IS_AUDIO_SEGMENT_CLIP_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIO_SEGMENT_CLIP)) + +typedef struct _GstAudioSegmentClip GstAudioSegmentClip; +typedef struct _GstAudioSegmentClipClass GstAudioSegmentClipClass; + +struct _GstAudioSegmentClip +{ + GstSegmentClip parent; + + /* < private > */ + gint rate; + gint framesize; +}; + +struct _GstAudioSegmentClipClass +{ + GstSegmentClipClass parent_class; +}; + +GType gst_audio_segment_clip_get_type (void); + +G_END_DECLS + +#endif /* __GST_AUDIO_SEGMENT_CLIP_H__ */ diff --git a/gst/segmentclip/gstsegmentclip.c b/gst/segmentclip/gstsegmentclip.c new file mode 100644 index 0000000000..f4aae4f531 --- /dev/null +++ b/gst/segmentclip/gstsegmentclip.c @@ -0,0 +1,305 @@ +/* GStreamer + * Copyright (C) 2010 Sebastian Dröge + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstsegmentclip.h" + +static void gst_segment_clip_reset (GstSegmentClip * self); + +static GstStateChangeReturn gst_segment_clip_change_state (GstElement * element, + GstStateChange transition); + +static GstFlowReturn gst_segment_clip_sink_chain (GstPad * pad, + GstBuffer * buffer); +static gboolean gst_segment_clip_sink_setcaps (GstPad * pad, GstCaps * caps); +static GstFlowReturn gst_segment_clip_sink_bufferalloc (GstPad * pad, + guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf); + +static gboolean gst_segment_clip_event (GstPad * pad, GstEvent * event); +static GstCaps *gst_segment_clip_getcaps (GstPad * pad); +static gboolean gst_segment_clip_query (GstPad * pad, GstQuery * query); +static const GstQueryType *gst_segment_clip_query_type (GstPad * pad); + +GST_DEBUG_CATEGORY_STATIC (gst_segment_clip_debug); +#define GST_CAT_DEFAULT gst_segment_clip_debug + +GST_BOILERPLATE (GstSegmentClip, gst_segment_clip, GstElement, + GST_TYPE_ELEMENT); + +static void +gst_segment_clip_base_init (gpointer g_class) +{ +} + +static void +gst_segment_clip_class_init (GstSegmentClipClass * klass) +{ + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + + GST_DEBUG_CATEGORY_INIT (gst_segment_clip_debug, "segmentclip", 0, + "segmentclip base class"); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_segment_clip_change_state); +} + +static void +gst_segment_clip_init (GstSegmentClip * self, GstSegmentClipClass * g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + GstPadTemplate *templ; + + templ = gst_element_class_get_pad_template (element_class, "sink"); + g_assert (templ); + + self->sinkpad = gst_pad_new_from_template (templ, "sink"); + gst_pad_set_chain_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_segment_clip_sink_chain)); + gst_pad_set_event_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_segment_clip_event)); + gst_pad_set_setcaps_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_segment_clip_sink_setcaps)); + gst_pad_set_getcaps_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_segment_clip_getcaps)); + gst_pad_set_bufferalloc_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_segment_clip_sink_bufferalloc)); + gst_pad_set_query_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_segment_clip_query)); + gst_pad_set_query_type_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_segment_clip_query_type)); + gst_element_add_pad (GST_ELEMENT (self), self->sinkpad); + + gst_object_unref (templ); + + templ = gst_element_class_get_pad_template (element_class, "src"); + g_assert (templ); + + self->srcpad = gst_pad_new_from_template (templ, "src"); + gst_pad_set_event_function (self->srcpad, + GST_DEBUG_FUNCPTR (gst_segment_clip_event)); + gst_pad_set_getcaps_function (self->srcpad, + GST_DEBUG_FUNCPTR (gst_segment_clip_getcaps)); + gst_pad_set_query_function (self->srcpad, + GST_DEBUG_FUNCPTR (gst_segment_clip_query)); + gst_pad_set_query_type_function (self->srcpad, + GST_DEBUG_FUNCPTR (gst_segment_clip_query_type)); + gst_element_add_pad (GST_ELEMENT (self), self->srcpad); + + gst_segment_clip_reset (self); +} + +static void +gst_segment_clip_reset (GstSegmentClip * self) +{ + GstSegmentClipClass *klass = GST_SEGMENT_CLIP_GET_CLASS (self); + + GST_DEBUG_OBJECT (self, "Resetting internal state"); + + gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED); + if (klass->reset) + klass->reset (self); +} + +static GstFlowReturn +gst_segment_clip_sink_bufferalloc (GstPad * pad, guint64 offset, guint size, + GstCaps * caps, GstBuffer ** buf) +{ + GstSegmentClip *self = GST_SEGMENT_CLIP (gst_pad_get_parent (pad)); + GstFlowReturn ret = GST_FLOW_OK; + + GST_LOG_OBJECT (pad, "Allocating buffer with offset 0x%" G_GINT64_MODIFIER + "x and size %u with caps: %" GST_PTR_FORMAT, offset, size, caps); + + *buf = NULL; + + ret = gst_pad_alloc_buffer (self->srcpad, offset, size, caps, buf); + if (G_UNLIKELY (ret != GST_FLOW_OK)) + GST_ERROR_OBJECT (pad, "Allocating buffer failed: %s", + gst_flow_get_name (ret)); + + gst_object_unref (self); + + return ret; +} + +static gboolean +gst_segment_clip_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstSegmentClip *self = GST_SEGMENT_CLIP (gst_pad_get_parent (pad)); + GstSegmentClipClass *klass = GST_SEGMENT_CLIP_GET_CLASS (self); + gboolean ret; + + GST_DEBUG_OBJECT (pad, "Setting caps: %" GST_PTR_FORMAT, caps); + ret = klass->set_caps (self, caps); + + gst_object_unref (self); + + return ret; +} + +static GstCaps * +gst_segment_clip_getcaps (GstPad * pad) +{ + GstSegmentClip *self = GST_SEGMENT_CLIP (gst_pad_get_parent (pad)); + GstPad *otherpad; + GstCaps *tmp, *ret; + + otherpad = (pad == self->srcpad) ? self->sinkpad : self->srcpad; + + tmp = gst_pad_peer_get_caps (otherpad); + if (tmp) { + ret = gst_caps_intersect (tmp, gst_pad_get_pad_template_caps (pad)); + gst_caps_unref (tmp); + } else { + ret = gst_caps_copy (gst_pad_get_pad_template_caps (pad)); + } + + gst_object_unref (self); + + GST_LOG_OBJECT (pad, "Returning caps: %" GST_PTR_FORMAT, ret); + + return ret; +} + +static gboolean +gst_segment_clip_query (GstPad * pad, GstQuery * query) +{ + GstSegmentClip *self = GST_SEGMENT_CLIP (gst_pad_get_parent (pad)); + gboolean ret; + GstPad *otherpad; + + otherpad = (pad == self->srcpad) ? self->sinkpad : self->srcpad; + + GST_LOG_OBJECT (pad, "Handling query of type '%s'", + gst_query_type_get_name (GST_QUERY_TYPE (query))); + + ret = gst_pad_peer_query (otherpad, query); + + gst_object_unref (self); + return ret; +} + +static const GstQueryType * +gst_segment_clip_query_type (GstPad * pad) +{ + GstSegmentClip *self = GST_SEGMENT_CLIP (gst_pad_get_parent (pad)); + GstPad *otherpad, *peer; + const GstQueryType *types = NULL; + + otherpad = (pad == self->srcpad) ? self->sinkpad : self->srcpad; + peer = gst_pad_get_peer (otherpad); + if (peer) { + types = gst_pad_get_query_types (peer); + gst_object_unref (peer); + } + + gst_object_unref (self); + + return types; +} + + +static GstFlowReturn +gst_segment_clip_sink_chain (GstPad * pad, GstBuffer * buffer) +{ + GstSegmentClip *self = GST_SEGMENT_CLIP (GST_PAD_PARENT (pad)); + GstFlowReturn ret; + GstSegmentClipClass *klass = GST_SEGMENT_CLIP_GET_CLASS (self); + GstBuffer *outbuf = NULL; + + GST_LOG_OBJECT (pad, + "Handling buffer with timestamp %" GST_TIME_FORMAT " and duration %" + GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), + GST_TIME_ARGS (GST_BUFFER_DURATION (buffer))); + + ret = klass->clip_buffer (self, buffer, &outbuf); + if (ret == GST_FLOW_OK && outbuf) + ret = gst_pad_push (self->srcpad, outbuf); + + return ret; +} + +static GstStateChangeReturn +gst_segment_clip_change_state (GstElement * element, GstStateChange transition) +{ + GstSegmentClip *self = GST_SEGMENT_CLIP (element); + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + if (GST_ELEMENT_CLASS (parent_class)->change_state) + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_segment_clip_reset (self); + break; + default: + break; + } + + return ret; +} + +static gboolean +gst_segment_clip_event (GstPad * pad, GstEvent * event) +{ + GstSegmentClip *self = GST_SEGMENT_CLIP (gst_pad_get_parent (pad)); + GstPad *otherpad; + gboolean ret; + + GST_LOG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event)); + + otherpad = (pad == self->srcpad) ? self->sinkpad : self->srcpad; + ret = gst_pad_push_event (otherpad, event); + + if (ret) { + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_NEWSEGMENT:{ + GstFormat fmt; + gboolean is_update; + gint64 start, end, base; + gdouble rate; + + gst_event_parse_new_segment (event, &is_update, &rate, &fmt, &start, + &end, &base); + GST_DEBUG_OBJECT (pad, + "Got NEWSEGMENT event in %s format, passing on (%" + GST_TIME_FORMAT " - %" GST_TIME_FORMAT ")", + gst_format_get_name (fmt), GST_TIME_ARGS (start), + GST_TIME_ARGS (end)); + gst_segment_set_newsegment (&self->segment, is_update, rate, fmt, start, + end, base); + break; + } + case GST_EVENT_FLUSH_STOP: + gst_segment_clip_reset (self); + break; + default: + break; + } + } + + gst_object_unref (self); + return ret; +} diff --git a/gst/segmentclip/gstsegmentclip.h b/gst/segmentclip/gstsegmentclip.h new file mode 100644 index 0000000000..0a9f077b7e --- /dev/null +++ b/gst/segmentclip/gstsegmentclip.h @@ -0,0 +1,66 @@ +/* GStreamer + * Copyright (C) 2010 Sebastian Dröge + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_SEGMENT_CLIP_H__ +#define __GST_SEGMENT_CLIP_H__ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_SEGMENT_CLIP \ + (gst_segment_clip_get_type()) +#define GST_SEGMENT_CLIP(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SEGMENT_CLIP,GstSegmentClip)) +#define GST_SEGMENT_CLIP_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SEGMENT_CLIP,GstSegmentClipClass)) +#define GST_SEGMENT_CLIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_SEGMENT_CLIP,GstSegmentClipClass)) +#define GST_IS_SEGMENT_CLIP(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SEGMENT_CLIP)) +#define GST_IS_SEGMENT_CLIP_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SEGMENT_CLIP)) + +typedef struct _GstSegmentClip GstSegmentClip; +typedef struct _GstSegmentClipClass GstSegmentClipClass; + +struct _GstSegmentClip +{ + GstElement parent; + + /* < private > */ + GstPad *sinkpad; + GstPad *srcpad; + + GstSegment segment; +}; + +struct _GstSegmentClipClass +{ + GstElementClass parent_class; + + gboolean (*set_caps) (GstSegmentClip *self, GstCaps *caps); + GstFlowReturn (*clip_buffer) (GstSegmentClip *self, GstBuffer *buffer, GstBuffer **outbuf); + void (*reset) (GstSegmentClip *self); +}; + +GType gst_segment_clip_get_type (void); + +G_END_DECLS + +#endif /* __GST_SEGMENT_CLIP_H__ */ diff --git a/gst/segmentclip/gstvideosegmentclip.c b/gst/segmentclip/gstvideosegmentclip.c new file mode 100644 index 0000000000..16a948e7f2 --- /dev/null +++ b/gst/segmentclip/gstvideosegmentclip.c @@ -0,0 +1,193 @@ +/* GStreamer + * Copyright (C) 2010 Sebastian Dröge + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "gstvideosegmentclip.h" + +static GstStaticPadTemplate sink_pad_template = + GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS + ("video/x-raw-yuv, framerate=" GST_VIDEO_FPS_RANGE "; " + "video/x-raw-rgb, framerate=" GST_VIDEO_FPS_RANGE "; " + "video/x-raw-grey, framerate=" GST_VIDEO_FPS_RANGE)); + + +static GstStaticPadTemplate src_pad_template = + GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS + ("video/x-raw-yuv, framerate=" GST_VIDEO_FPS_RANGE "; " + "video/x-raw-rgb, framerate=" GST_VIDEO_FPS_RANGE "; " + "video/x-raw-grey, framerate=" GST_VIDEO_FPS_RANGE)); + +static void gst_video_segment_clip_reset (GstSegmentClip * self); +static GstFlowReturn gst_video_segment_clip_clip_buffer (GstSegmentClip * self, + GstBuffer * buffer, GstBuffer ** outbuf); +static gboolean gst_video_segment_clip_set_caps (GstSegmentClip * self, + GstCaps * caps); + +GST_DEBUG_CATEGORY_STATIC (gst_video_segment_clip_debug); +#define GST_CAT_DEFAULT gst_video_segment_clip_debug + +GST_BOILERPLATE (GstVideoSegmentClip, gst_video_segment_clip, GstSegmentClip, + GST_TYPE_SEGMENT_CLIP); + +static void +gst_video_segment_clip_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details_simple (element_class, + "Video buffer segment clipper", + "Filter/Video", + "Clips video buffers to the configured segment", + "Sebastian Dröge "); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_pad_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_pad_template)); +} + +static void +gst_video_segment_clip_class_init (GstVideoSegmentClipClass * klass) +{ + GstSegmentClipClass *segment_clip_klass = GST_SEGMENT_CLIP_CLASS (klass); + + GST_DEBUG_CATEGORY_INIT (gst_video_segment_clip_debug, "videosegmentclip", 0, + "videosegmentclip element"); + + segment_clip_klass->reset = GST_DEBUG_FUNCPTR (gst_video_segment_clip_reset); + segment_clip_klass->set_caps = + GST_DEBUG_FUNCPTR (gst_video_segment_clip_set_caps); + segment_clip_klass->clip_buffer = + GST_DEBUG_FUNCPTR (gst_video_segment_clip_clip_buffer); +} + +static void +gst_video_segment_clip_init (GstVideoSegmentClip * self, + GstVideoSegmentClipClass * g_class) +{ +} + +static void +gst_video_segment_clip_reset (GstSegmentClip * base) +{ + GstVideoSegmentClip *self = GST_VIDEO_SEGMENT_CLIP (base); + + GST_DEBUG_OBJECT (self, "Resetting internal state"); + + self->fps_n = self->fps_d = 0; +} + + +static GstFlowReturn +gst_video_segment_clip_clip_buffer (GstSegmentClip * base, GstBuffer * buffer, + GstBuffer ** outbuf) +{ + GstVideoSegmentClip *self = GST_VIDEO_SEGMENT_CLIP (base); + GstSegment *segment = &base->segment; + GstClockTime timestamp, duration; + gint64 cstart, cstop; + gboolean in_seg; + + if (!self->fps_d) { + GST_ERROR_OBJECT (self, "Not negotiated yet"); + gst_buffer_unref (buffer); + return GST_FLOW_NOT_NEGOTIATED; + } + + if (segment->format != GST_FORMAT_TIME) { + GST_DEBUG_OBJECT (self, "Unsupported segment format %s", + gst_format_get_name (segment->format)); + *outbuf = buffer; + return GST_FLOW_OK; + } + + if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) { + GST_WARNING_OBJECT (self, "Buffer without valid timestamp"); + *outbuf = buffer; + return GST_FLOW_OK; + } + + if (self->fps_n == 0) { + *outbuf = buffer; + return GST_FLOW_OK; + } + + timestamp = GST_BUFFER_TIMESTAMP (buffer); + duration = GST_BUFFER_DURATION (buffer); + if (!GST_CLOCK_TIME_IS_VALID (duration)) + duration = gst_util_uint64_scale (GST_SECOND, self->fps_d, self->fps_n); + + in_seg = + gst_segment_clip (segment, GST_FORMAT_TIME, timestamp, + timestamp + duration, &cstart, &cstop); + if (in_seg) { + if (timestamp != cstart || timestamp + duration != cstop) { + *outbuf = gst_buffer_make_metadata_writable (buffer); + + GST_BUFFER_TIMESTAMP (*outbuf) = cstart; + GST_BUFFER_DURATION (*outbuf) = cstop - cstart; + } else { + *outbuf = buffer; + } + } else { + GST_DEBUG_OBJECT (self, "Buffer outside the configured segment"); + + if (segment->rate >= 0) { + if (segment->stop != -1 && timestamp >= segment->stop) + return GST_FLOW_UNEXPECTED; + } else { + if (segment->start != -1 && timestamp + duration <= segment->start) + return GST_FLOW_UNEXPECTED; + } + gst_buffer_unref (buffer); + } + + + return GST_FLOW_OK; +} + +static gboolean +gst_video_segment_clip_set_caps (GstSegmentClip * base, GstCaps * caps) +{ + GstVideoSegmentClip *self = GST_VIDEO_SEGMENT_CLIP (base); + gboolean ret; + GstStructure *s; + gint fps_n, fps_d; + + s = gst_caps_get_structure (caps, 0); + + ret = gst_structure_get_fraction (s, "framerate", &fps_n, &fps_d); + ret = (fps_d != 0); + + if (ret) { + GST_DEBUG_OBJECT (self, "Configured framerate %d/%d", fps_n, fps_d); + self->fps_n = fps_n; + self->fps_d = fps_d; + } + + return ret; +} diff --git a/gst/segmentclip/gstvideosegmentclip.h b/gst/segmentclip/gstvideosegmentclip.h new file mode 100644 index 0000000000..1f00445fbe --- /dev/null +++ b/gst/segmentclip/gstvideosegmentclip.h @@ -0,0 +1,60 @@ +/* GStreamer + * Copyright (C) 2010 Sebastian Dröge + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_VIDEO_SEGMENT_CLIP_H__ +#define __GST_VIDEO_SEGMENT_CLIP_H__ + +#include +#include "gstsegmentclip.h" + +G_BEGIN_DECLS + +#define GST_TYPE_VIDEO_SEGMENT_CLIP \ + (gst_video_segment_clip_get_type()) +#define GST_VIDEO_SEGMENT_CLIP(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_SEGMENT_CLIP,GstVideoSegmentClip)) +#define GST_VIDEO_SEGMENT_CLIP_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_SEGMENT_CLIP,GstVideoSegmentClipClass)) +#define GST_VIDEO_SEGMENT_CLIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_VIDEO_SEGMENT_CLIP,GstVideoSegmentClipClass)) +#define GST_IS_VIDEO_SEGMENT_CLIP(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_SEGMENT_CLIP)) +#define GST_IS_VIDEO_SEGMENT_CLIP_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_SEGMENT_CLIP)) + +typedef struct _GstVideoSegmentClip GstVideoSegmentClip; +typedef struct _GstVideoSegmentClipClass GstVideoSegmentClipClass; + +struct _GstVideoSegmentClip +{ + GstSegmentClip parent; + + /* < private > */ + gint fps_n, fps_d; +}; + +struct _GstVideoSegmentClipClass +{ + GstSegmentClipClass parent_class; +}; + +GType gst_video_segment_clip_get_type (void); + +G_END_DECLS + +#endif /* __GST_VIDEO_SEGMENT_CLIP_H__ */ diff --git a/gst/segmentclip/plugin.c b/gst/segmentclip/plugin.c new file mode 100644 index 0000000000..c64f5c645e --- /dev/null +++ b/gst/segmentclip/plugin.c @@ -0,0 +1,43 @@ +/* GStreamer + * Copyright (C) 2010 Sebastian Dröge + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstaudiosegmentclip.h" +#include "gstvideosegmentclip.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_element_register (plugin, "audiosegmentclip", GST_RANK_NONE, + GST_TYPE_AUDIO_SEGMENT_CLIP) || + !gst_element_register (plugin, "videosegmentclip", GST_RANK_NONE, + GST_TYPE_VIDEO_SEGMENT_CLIP)) + return FALSE; + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "segmentclip", + "Segment clip elements", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)