summaryrefslogtreecommitdiffstats
path: root/gst
diff options
context:
space:
mode:
authorDave Robillard <dave@drobilla.net>2009-06-19 21:01:54 -0400
committerDave Robillard <dave@drobilla.net>2009-06-19 21:01:54 -0400
commit925e83ee60c5406b2e5f0f39b0da0f90370efc27 (patch)
treece01d298ed0c5d31853fa43aedc2af0779417816 /gst
parent7f3bcd484b465d8216ac419754450adf07e9b0d2 (diff)
parentc70dbe94b5ff9a0993d852605d40c21020c59552 (diff)
downloadgst-plugins-bad-925e83ee60c5406b2e5f0f39b0da0f90370efc27.tar.gz
gst-plugins-bad-925e83ee60c5406b2e5f0f39b0da0f90370efc27.tar.bz2
gst-plugins-bad-925e83ee60c5406b2e5f0f39b0da0f90370efc27.zip
Merge branch 'master' of git://anongit.freedesktop.org/gstreamer/gst-plugins-bad into fdo
Diffstat (limited to 'gst')
-rw-r--r--gst/aacparse/gstaacparse.c8
-rw-r--r--gst/camerabin/Makefile.am8
-rw-r--r--gst/camerabin/camerabingeneral.c5
-rw-r--r--gst/camerabin/camerabinimage.c107
-rw-r--r--gst/camerabin/camerabinimage.h15
-rw-r--r--gst/camerabin/camerabinpreview.c257
-rw-r--r--gst/camerabin/camerabinpreview.h37
-rw-r--r--gst/camerabin/camerabinvideo.c10
-rw-r--r--gst/camerabin/gstcamerabin.c681
-rw-r--r--gst/camerabin/gstcamerabin.h25
-rw-r--r--gst/camerabin/gstcamerabinphotography.c319
-rw-r--r--gst/camerabin/gstcamerabinxoverlay.c73
-rw-r--r--gst/camerabin/gstcamerabinxoverlay.h28
-rw-r--r--gst/debugutils/Makefile.am2
-rw-r--r--gst/dvdspu/gstdvdspu.c5
-rw-r--r--gst/frei0r/Makefile.am15
-rw-r--r--gst/frei0r/frei0r.h567
-rw-r--r--gst/frei0r/gstfrei0r.c598
-rw-r--r--gst/frei0r/gstfrei0r.h91
-rw-r--r--gst/frei0r/gstfrei0rfilter.c241
-rw-r--r--gst/frei0r/gstfrei0rfilter.h63
-rw-r--r--gst/frei0r/gstfrei0rmixer.c782
-rw-r--r--gst/frei0r/gstfrei0rmixer.h75
-rw-r--r--gst/frei0r/gstfrei0rsrc.c411
-rw-r--r--gst/frei0r/gstfrei0rsrc.h69
-rw-r--r--gst/hdvparse/Makefile.am2
-rw-r--r--gst/hdvparse/gsthdvparse.c893
-rw-r--r--gst/id3tag/Makefile.am6
-rw-r--r--gst/id3tag/gstid3mux.c (renamed from gst/id3tag/gstid3tag.c)66
-rw-r--r--gst/id3tag/gstid3mux.h (renamed from gst/id3tag/gstid3tag.h)36
-rw-r--r--gst/id3tag/gsttagmux.c10
-rw-r--r--gst/id3tag/id3tag.c46
-rw-r--r--gst/id3tag/id3tag.h4
-rw-r--r--gst/liveadder/liveadder.c5
-rw-r--r--gst/mpegdemux/gstmpegdefs.h9
-rw-r--r--gst/mpegdemux/gstmpegdesc.c4
-rw-r--r--gst/mpegdemux/gstmpegtsdemux.c19
-rw-r--r--gst/mpegtsmux/mpegtsmux.c12
-rw-r--r--gst/mpegvideoparse/mpegvideoparse.c69
-rw-r--r--gst/mxf/mxfdemux.c8
-rw-r--r--gst/nsf/nes6502.c4
-rw-r--r--gst/qtmux/atoms.c186
-rw-r--r--gst/qtmux/atoms.h21
-rw-r--r--gst/qtmux/fourcc.h10
-rw-r--r--gst/qtmux/gstqtmux.c542
-rw-r--r--gst/qtmux/gstqtmux.h2
-rw-r--r--gst/qtmux/gstqtmuxmap.c2
-rw-r--r--gst/rtpmanager/gstrtpjitterbuffer.c5
-rw-r--r--gst/rtpmanager/gstrtpsession.c62
-rw-r--r--gst/rtpmanager/rtpsession.c37
-rw-r--r--gst/rtpmanager/rtpsession.h4
-rw-r--r--gst/rtpmanager/rtpsource.c118
-rw-r--r--gst/rtpmanager/rtpsource.h2
-rw-r--r--gst/scaletempo/gstscaletempo.c2
-rw-r--r--gst/selector/gstinputselector.c3
-rw-r--r--gst/selector/gstoutputselector.c51
-rw-r--r--gst/shapewipe/gstshapewipe.c204
-rw-r--r--gst/shapewipe/gstshapewipe.h4
58 files changed, 5958 insertions, 982 deletions
diff --git a/gst/aacparse/gstaacparse.c b/gst/aacparse/gstaacparse.c
index 07906347..6db4b619 100644
--- a/gst/aacparse/gstaacparse.c
+++ b/gst/aacparse/gstaacparse.c
@@ -664,8 +664,12 @@ gst_aacparse_parse_frame (GstBaseParse * parse, GstBuffer * buffer)
}
}
- GST_BUFFER_DURATION (buffer) = AAC_FRAME_DURATION (aacparse);
- GST_BUFFER_TIMESTAMP (buffer) = aacparse->ts;
+ /* ADIF: only send an initial 0 timestamp downstream,
+ * then admit we have no idea and let downstream (decoder) handle it */
+ if (aacparse->header_type != DSPAAC_HEADER_ADIF || !aacparse->ts) {
+ GST_BUFFER_DURATION (buffer) = AAC_FRAME_DURATION (aacparse);
+ GST_BUFFER_TIMESTAMP (buffer) = aacparse->ts;
+ }
if (GST_CLOCK_TIME_IS_VALID (aacparse->ts))
aacparse->ts += GST_BUFFER_DURATION (buffer);
diff --git a/gst/camerabin/Makefile.am b/gst/camerabin/Makefile.am
index 3691af85..d5085da9 100644
--- a/gst/camerabin/Makefile.am
+++ b/gst/camerabin/Makefile.am
@@ -14,28 +14,28 @@ EXTRA_DIST = gstcamerabin-marshal.list
plugin_LTLIBRARIES = libgstcamerabin.la
libgstcamerabin_la_SOURCES = gstcamerabin.c \
- gstcamerabinxoverlay.c \
gstcamerabincolorbalance.c \
camerabinimage.c \
camerabinvideo.c \
camerabingeneral.c \
+ camerabinpreview.c \
gstcamerabinphotography.c
nodist_libgstcamerabin_la_SOURCES = $(built_sources)
libgstcamerabin_la_CFLAGS = \
$(GST_CFLAGS) $(GST_BASE_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) -DGST_USE_UNSTABLE_API
libgstcamerabin_la_LIBADD = \
+ $(top_builddir)/gst-libs/gst/interfaces/libgstphotography-$(GST_MAJORMINOR).la \
$(GST_LIBS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) \
- -lgstinterfaces-$(GST_MAJORMINOR) \
- $(top_builddir)/gst-libs/gst/interfaces/libgstphotography-$(GST_MAJORMINOR).la
+ -lgstinterfaces-$(GST_MAJORMINOR)
libgstcamerabin_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstcamerabin_la_LIBTOOLFLAGS = --tag=disable-static
noinst_HEADERS = gstcamerabin.h \
- gstcamerabinxoverlay.h \
gstcamerabincolorbalance.h \
camerabinimage.h \
camerabinvideo.h \
camerabingeneral.h \
+ camerabinpreview.h \
gstcamerabinphotography.h
diff --git a/gst/camerabin/camerabingeneral.c b/gst/camerabin/camerabingeneral.c
index 627e0328..d9d9a202 100644
--- a/gst/camerabin/camerabingeneral.c
+++ b/gst/camerabin/camerabingeneral.c
@@ -150,9 +150,9 @@ gst_camerabin_try_add_element (GstBin * bin, GstElement * new_elem)
}
/* Get pads for linking */
- GST_DEBUG ("finding unconnected src pad");
bin_pad = gst_bin_find_unlinked_pad (bin, GST_PAD_SRC);
- GST_DEBUG ("unconnected pad %s:%s", GST_DEBUG_PAD_NAME (bin_pad));
+ GST_DEBUG ("adding %" GST_PTR_FORMAT " to %s:%s", new_elem,
+ GST_DEBUG_PAD_NAME (bin_pad));
/* Add to bin */
gst_bin_add (GST_BIN (bin), new_elem);
/* Link, if unconnected pad was found, otherwise just add it to bin */
@@ -185,7 +185,6 @@ gst_camerabin_create_and_add_element (GstBin * bin, const gchar * elem_name)
{
GstElement *new_elem = NULL;
- GST_DEBUG ("adding %s", elem_name);
new_elem = gst_element_factory_make (elem_name, NULL);
if (!new_elem) {
GST_ELEMENT_ERROR (bin, CORE, MISSING_PLUGIN, (NULL),
diff --git a/gst/camerabin/camerabinimage.c b/gst/camerabin/camerabinimage.c
index e4ff6b86..da979f0f 100644
--- a/gst/camerabin/camerabinimage.c
+++ b/gst/camerabin/camerabinimage.c
@@ -30,17 +30,13 @@
* <informalexample>
* <programlisting>
*-----------------------------------------------------------------------------
- * (src0) -> queue ->
- * -> [post proc] -> tee <
- * (src1) -> imageenc -> metadatamuxer -> filesink
+ *
+ * -> [post proc] -> csp -> imageenc -> metadatamuxer -> filesink
+ *
*-----------------------------------------------------------------------------
* </programlisting>
* </informalexample>
*
- * The property of elements are:
- *
- * queue - "max-size-buffers", 1, "leaky", 2,
- *
* The image bin opens file for image writing in READY to PAUSED state change.
* The image bin closes the file in PAUSED to READY state change.
*
@@ -150,24 +146,16 @@ gst_camerabin_image_init (GstCameraBinImage * img,
{
img->filename = g_string_new ("");
- img->pad_tee_enc = NULL;
- img->pad_tee_view = NULL;
-
img->post = NULL;
- img->tee = NULL;
img->enc = NULL;
img->user_enc = NULL;
img->meta_mux = NULL;
img->sink = NULL;
- img->queue = NULL;
/* Create src and sink ghost pads */
img->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
gst_element_add_pad (GST_ELEMENT (img), img->sinkpad);
- img->srcpad = gst_ghost_pad_new_no_target ("src", GST_PAD_SRC);
- gst_element_add_pad (GST_ELEMENT (img), img->srcpad);
-
img->elements_created = FALSE;
}
@@ -196,6 +184,7 @@ gst_camerabin_image_change_state (GstElement * element,
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GstCameraBinImage *img = GST_CAMERABIN_IMAGE (element);
+ GstObject *camerabin = NULL;
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
@@ -204,15 +193,25 @@ gst_camerabin_image_change_state (GstElement * element,
}
/* Allow setting filename when image bin in READY state */
gst_element_set_locked_state (img->sink, TRUE);
+ GST_INFO_OBJECT (img, "locking imagebin->sink state to %s",
+ gst_element_state_get_name (GST_STATE (img->sink)));
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
- gst_element_set_locked_state (img->sink, FALSE);
+ if (!g_str_equal (img->filename->str, "")) {
+ GST_INFO_OBJECT (img, "preparing image with filename: %s",
+ img->filename->str);
+ gst_element_set_locked_state (img->sink, FALSE);
+ } else {
+ GST_INFO_OBJECT (img, "keep sink locked, we have no filename yet");
+ }
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
/* Set sink to NULL in order to write the file _now_ */
- GST_INFO ("write img file: %s", img->filename->str);
+ GST_INFO_OBJECT (img, "write image with filename: %s",
+ img->filename->str);
gst_element_set_locked_state (img->sink, TRUE);
gst_element_set_state (img->sink, GST_STATE_NULL);
+ g_string_assign (img->filename, "");
break;
default:
break;
@@ -221,6 +220,14 @@ gst_camerabin_image_change_state (GstElement * element,
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ camerabin = gst_element_get_parent (img);
+ /* Write debug graph to file */
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (camerabin),
+ GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE |
+ GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS, "imagebin.playing");
+ gst_object_unref (camerabin);
+ break;
case GST_STATE_CHANGE_READY_TO_NULL:
gst_camerabin_image_destroy_elements (img);
break;
@@ -261,11 +268,18 @@ gst_camerabin_image_set_property (GObject * object, guint prop_id,
switch (prop_id) {
case PROP_FILENAME:
g_string_assign (bin->filename, g_value_get_string (value));
+ GST_INFO_OBJECT (bin, "received filename: '%s'", bin->filename->str);
if (bin->sink) {
- g_object_set (G_OBJECT (bin->sink), "location", bin->filename->str,
- NULL);
+ if (!g_str_equal (bin->filename->str, "")) {
+ g_object_set (G_OBJECT (bin->sink), "location", bin->filename->str,
+ NULL);
+ gst_element_set_locked_state (bin->sink, FALSE);
+ gst_element_sync_state_with_parent (bin->sink);
+ } else {
+ GST_INFO_OBJECT (bin, "empty filename");
+ }
} else {
- GST_INFO ("no sink, not setting name yet");
+ GST_INFO_OBJECT (bin, "no sink, not setting name yet");
}
break;
default:
@@ -369,17 +383,17 @@ done:
* Use gst_camerabin_image_destroy_elements to release these resources.
*
* Image bin:
- * img->sinkpad ! [ post process !] tee name=t0 ! encoder ! metadata ! filesink
- * t0. ! queue ! img->srcpad
+ * img->sinkpad ! [ post process !] csp ! encoder ! metadata ! filesink
*
* Returns: %TRUE if succeeded or FALSE if failed
*/
static gboolean
gst_camerabin_image_create_elements (GstCameraBinImage * img)
{
- GstPad *sinkpad = NULL, *img_sinkpad = NULL, *img_srcpad = NULL;
+ GstPad *sinkpad = NULL, *img_sinkpad = NULL;
gboolean ret = FALSE;
GstBin *imgbin = NULL;
+ GstElement *csp = NULL;
g_return_val_if_fail (img != NULL, FALSE);
@@ -403,23 +417,18 @@ gst_camerabin_image_create_elements (GstCameraBinImage * img)
img_sinkpad = gst_element_get_static_pad (img->post, "sink");
}
- /* Create tee */
- if (!(img->tee = gst_camerabin_create_and_add_element (imgbin, "tee"))) {
+ /* Add colorspace converter */
+ if (!(csp =
+ gst_camerabin_create_and_add_element (imgbin, "ffmpegcolorspace"))) {
goto done;
}
/* Set up sink ghost pad for img bin */
if (!img_sinkpad) {
- img_sinkpad = gst_element_get_static_pad (img->tee, "sink");
+ img_sinkpad = gst_element_get_static_pad (csp, "sink");
}
gst_ghost_pad_set_target (GST_GHOST_PAD (img->sinkpad), img_sinkpad);
- /* Add colorspace converter */
- img->pad_tee_enc = gst_element_get_request_pad (img->tee, "src%d");
- if (!gst_camerabin_create_and_add_element (imgbin, "ffmpegcolorspace")) {
- goto done;
- }
-
/* Create image encoder */
if (img->user_enc) {
img->enc = img->user_enc;
@@ -452,33 +461,14 @@ gst_camerabin_image_create_elements (GstCameraBinImage * img)
goto done;
}
- /* Create queue element leading to view finder, attaches it to the tee */
- img->pad_tee_view = gst_element_get_request_pad (img->tee, "src%d");
- if (!(img->queue = gst_camerabin_create_and_add_element (imgbin, "queue"))) {
- goto done;
- }
-
/* Set properties */
g_object_set (G_OBJECT (img->sink), "location", img->filename->str, NULL);
g_object_set (G_OBJECT (img->sink), "async", FALSE, NULL);
- g_object_set (G_OBJECT (img->queue), "max-size-buffers", 1, "leaky", 2, NULL);
-
- /* Set up src ghost pad for img bin */
- img_srcpad = gst_element_get_static_pad (img->queue, "src");
- gst_ghost_pad_set_target (GST_GHOST_PAD (img->srcpad), img_srcpad);
-
- /* Never let image bin eos events reach view finder */
- gst_pad_add_event_probe (img->srcpad,
- G_CALLBACK (gst_camerabin_drop_eos_probe), img);
-
ret = TRUE;
done:
- if (img_srcpad) {
- gst_object_unref (img_srcpad);
- }
if (img_sinkpad) {
gst_object_unref (img_sinkpad);
}
@@ -502,27 +492,14 @@ static void
gst_camerabin_image_destroy_elements (GstCameraBinImage * img)
{
GST_LOG ("destroying img elements");
- if (img->pad_tee_enc) {
- gst_element_release_request_pad (img->tee, img->pad_tee_enc);
- img->pad_tee_enc = NULL;
- }
-
- if (img->pad_tee_view) {
- gst_element_release_request_pad (img->tee, img->pad_tee_view);
- img->pad_tee_view = NULL;
- }
gst_ghost_pad_set_target (GST_GHOST_PAD (img->sinkpad), NULL);
- gst_ghost_pad_set_target (GST_GHOST_PAD (img->srcpad), NULL);
gst_camerabin_remove_elements_from_bin (GST_BIN (img));
- img->post = NULL;
- img->tee = NULL;
img->enc = NULL;
img->meta_mux = NULL;
img->sink = NULL;
- img->queue = NULL;
img->elements_created = FALSE;
}
@@ -530,6 +507,7 @@ gst_camerabin_image_destroy_elements (GstCameraBinImage * img)
void
gst_camerabin_image_set_encoder (GstCameraBinImage * img, GstElement * encoder)
{
+ GST_DEBUG ("setting image encoder %" GST_PTR_FORMAT, encoder);
if (img->user_enc)
gst_object_unref (img->user_enc);
if (encoder)
@@ -542,6 +520,7 @@ void
gst_camerabin_image_set_postproc (GstCameraBinImage * img,
GstElement * postproc)
{
+ GST_DEBUG ("setting image postprocessing element %" GST_PTR_FORMAT, postproc);
if (img->post)
gst_object_unref (img->post);
if (postproc)
diff --git a/gst/camerabin/camerabinimage.h b/gst/camerabin/camerabinimage.h
index c05f5498..8214e9cb 100644
--- a/gst/camerabin/camerabinimage.h
+++ b/gst/camerabin/camerabinimage.h
@@ -24,20 +24,17 @@
#include <gst/gstbin.h>
G_BEGIN_DECLS
-
#define GST_TYPE_CAMERABIN_IMAGE (gst_camerabin_image_get_type())
#define GST_CAMERABIN_IMAGE_CAST(obj) ((GstCameraBinImage*)(obj))
#define GST_CAMERABIN_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CAMERABIN_IMAGE,GstCameraBinImage))
#define GST_CAMERABIN_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CAMERABIN_IMAGE,GstCameraBinImageClass))
#define GST_IS_CAMERABIN_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CAMERABIN_IMAGE))
#define GST_IS_CAMERABIN_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CAMERABIN_IMAGE))
-
/**
* GstCameraBinImage:
*
* The opaque #GstCameraBinImage structure.
*/
-
typedef struct _GstCameraBinImage GstCameraBinImage;
typedef struct _GstCameraBinImageClass GstCameraBinImageClass;
@@ -48,21 +45,12 @@ struct _GstCameraBinImage
/* Ghost pads of image bin */
GstPad *sinkpad;
- GstPad *srcpad;
-
- /* Tee src pad leading to image encoder */
- GstPad *pad_tee_enc;
- /* Tee src pad leading to view finder */
- GstPad *pad_tee_view;
GstElement *post;
-
- GstElement *tee;
GstElement *enc;
GstElement *user_enc;
GstElement *meta_mux;
GstElement *sink;
- GstElement *queue;
gboolean elements_created;
};
@@ -86,5 +74,4 @@ GstElement *gst_camerabin_image_get_encoder (GstCameraBinImage * img);
GstElement *gst_camerabin_image_get_postproc (GstCameraBinImage * img);
G_END_DECLS
-
-#endif /* #ifndef __CAMERABIN_IMAGE_H__ */
+#endif /* #ifndef __CAMERABIN_IMAGE_H__ */
diff --git a/gst/camerabin/camerabinpreview.c b/gst/camerabin/camerabinpreview.c
new file mode 100644
index 00000000..b64b2143
--- /dev/null
+++ b/gst/camerabin/camerabinpreview.c
@@ -0,0 +1,257 @@
+/*
+* GStreamer
+* Copyright (C) 2009 Nokia Corporation <multimedia@maemo.org>
+*
+* 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 <gst/gst.h>
+#include <string.h>
+
+#include "camerabingeneral.h"
+#include "camerabinpreview.h"
+
+static void
+save_result (GstElement * sink, GstBuffer * buf, GstPad * pad, gpointer data)
+{
+ GstBuffer **p_buf = (GstBuffer **) data;
+
+ *p_buf = gst_buffer_ref (buf);
+
+ GST_DEBUG ("received converted buffer %p with caps %" GST_PTR_FORMAT,
+ *p_buf, GST_BUFFER_CAPS (*p_buf));
+}
+
+static gboolean
+create_element (const gchar * factory_name, const gchar * elem_name,
+ GstElement ** element, GError ** err)
+{
+ *element = gst_element_factory_make (factory_name, elem_name);
+ if (*element)
+ return TRUE;
+
+ if (err && *err == NULL) {
+ *err = g_error_new (GST_CORE_ERROR, GST_CORE_ERROR_MISSING_PLUGIN,
+ "cannot create element '%s' - please check your GStreamer installation",
+ factory_name);
+ }
+
+ return FALSE;
+}
+
+
+/**
+ * gst_camerabin_preview_create_pipeline:
+ * @camera: camerabin object
+ *
+ * Create a preview converter pipeline.
+ *
+ * Returns: TRUE if pipeline was constructed, otherwise FALSE.
+ */
+gboolean
+gst_camerabin_preview_create_pipeline (GstCameraBin * camera)
+{
+ GstElement *src, *csp, *filter, *vscale, *sink;
+ GError *error = NULL;
+
+ if (!camera->preview_caps) {
+ return FALSE;
+ }
+
+ /* Destroy old pipeline, if any */
+ gst_camerabin_preview_destroy_pipeline (camera);
+
+ GST_DEBUG ("creating elements");
+
+ if (!create_element ("appsrc", "prev_src", &src, &error) ||
+ !create_element ("videoscale", NULL, &vscale, &error) ||
+ !create_element ("ffmpegcolorspace", NULL, &csp, &error) ||
+ !create_element ("capsfilter", NULL, &filter, &error) ||
+ !create_element ("fakesink", "prev_sink", &sink, &error))
+ goto no_elements;
+
+ camera->preview_pipeline = gst_pipeline_new ("preview-pipeline");
+ if (camera->preview_pipeline == NULL)
+ goto no_pipeline;
+
+ GST_DEBUG ("adding elements");
+ gst_bin_add_many (GST_BIN (camera->preview_pipeline),
+ src, csp, filter, vscale, sink, NULL);
+
+ g_object_set (filter, "caps", camera->preview_caps, NULL);
+ g_object_set (sink, "preroll-queue-len", 1, "signal-handoffs", TRUE, NULL);
+ g_object_set (vscale, "method", 0, NULL);
+
+ /* FIXME: linking is still way too expensive, profile this properly */
+ GST_DEBUG ("linking src->vscale");
+ if (!gst_element_link_pads (src, "src", vscale, "sink"))
+ return FALSE;
+
+ GST_DEBUG ("linking vscale->csp");
+ if (!gst_element_link_pads (vscale, "src", csp, "sink"))
+ return FALSE;
+
+ GST_DEBUG ("linking csp->capsfilter");
+ if (!gst_element_link_pads (csp, "src", filter, "sink"))
+ return FALSE;
+
+ GST_DEBUG ("linking capsfilter->sink");
+ if (!gst_element_link_pads (filter, "src", sink, "sink"))
+ return FALSE;
+
+ return TRUE;
+
+ /* ERRORS */
+no_elements:
+ {
+ g_warning ("Could not make preview pipeline: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+no_pipeline:
+ {
+ g_warning ("Could not make preview pipeline: %s",
+ "no pipeline (unknown error)");
+ return FALSE;
+ }
+}
+
+
+/**
+ * gst_camerabin_preview_destroy_pipeline:
+ * @camera: camerabin object
+ *
+ * Destroy preview converter pipeline.
+ */
+void
+gst_camerabin_preview_destroy_pipeline (GstCameraBin * camera)
+{
+ if (camera->preview_pipeline) {
+ gst_element_set_state (camera->preview_pipeline, GST_STATE_NULL);
+ gst_object_unref (camera->preview_pipeline);
+ camera->preview_pipeline = NULL;
+ }
+}
+
+
+/**
+ * gst_camerabin_preview_convert:
+ * @camera: camerabin object
+ * @buf: #GstBuffer that contains the frame to be converted
+ *
+ * Create a preview image of the given frame.
+ *
+ * Returns: converted preview image, or NULL if operation failed.
+ */
+GstBuffer *
+gst_camerabin_preview_convert (GstCameraBin * camera, GstBuffer * buf)
+{
+ GstMessage *msg;
+ GstBuffer *result = NULL;
+ GError *error = NULL;
+ GstBus *bus;
+ GstElement *src, *sink;
+ GstBufferFlag bflags;
+ GstFlowReturn fret;
+
+ g_return_val_if_fail (GST_BUFFER_CAPS (buf) != NULL, NULL);
+
+ if (camera->preview_pipeline == NULL) {
+ GST_WARNING ("pipeline is NULL");
+ goto no_pipeline;
+ }
+
+ src = gst_bin_get_by_name (GST_BIN (camera->preview_pipeline), "prev_src");
+ sink = gst_bin_get_by_name (GST_BIN (camera->preview_pipeline), "prev_sink");
+
+ if (!src || !sink) {
+ GST_WARNING ("pipeline doesn't have src / sink elements");
+ goto no_pipeline;
+ }
+
+ g_object_set (src, "size", (gint64) GST_BUFFER_SIZE (buf),
+ "blocksize", (guint32) GST_BUFFER_SIZE (buf),
+ "caps", GST_BUFFER_CAPS (buf), "num-buffers", 1, NULL);
+
+ g_signal_connect (sink, "handoff", G_CALLBACK (save_result), &result);
+
+ bflags = GST_BUFFER_FLAGS (buf);
+ GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_READONLY);
+
+ GST_DEBUG ("running conversion pipeline");
+ gst_element_set_state (camera->preview_pipeline, GST_STATE_PLAYING);
+
+ g_signal_emit_by_name (src, "push-buffer", buf, &fret);
+
+ /* TODO: do we need to use a bus poll, can we just register a callback to the bus? */
+ bus = gst_element_get_bus (camera->preview_pipeline);
+ msg =
+ gst_bus_poll (bus, GST_MESSAGE_ERROR | GST_MESSAGE_EOS, 25 * GST_SECOND);
+
+ if (msg) {
+ switch (GST_MESSAGE_TYPE (msg)) {
+ case GST_MESSAGE_EOS:{
+ if (result) {
+ GST_DEBUG ("preview image successful: result = %p", result);
+ } else {
+ GST_WARNING ("EOS but no result frame?!");
+ }
+ break;
+ }
+ case GST_MESSAGE_ERROR:{
+ gchar *dbg = NULL;
+
+ gst_message_parse_error (msg, &error, &dbg);
+ if (error) {
+ g_warning ("Could not make preview image: %s", error->message);
+ GST_DEBUG ("%s [debug: %s]", error->message, GST_STR_NULL (dbg));
+ g_error_free (error);
+ } else {
+ g_warning ("Could not make preview image (and NULL error!)");
+ }
+ g_free (dbg);
+ result = NULL;
+ break;
+ }
+ default:{
+ g_return_val_if_reached (NULL);
+ }
+ }
+ } else {
+ g_warning ("Could not make preview image: %s", "timeout during conversion");
+ result = NULL;
+ }
+
+ g_signal_handlers_disconnect_by_func (sink, G_CALLBACK (save_result),
+ &result);
+ gst_element_set_state (camera->preview_pipeline, GST_STATE_READY);
+
+ GST_BUFFER_FLAGS (buf) = bflags;
+
+ return result;
+
+ /* ERRORS */
+no_pipeline:
+ {
+ g_warning ("Could not make preview image: %s",
+ "no pipeline (unknown error)");
+ return NULL;
+ }
+}
diff --git a/gst/camerabin/camerabinpreview.h b/gst/camerabin/camerabinpreview.h
new file mode 100644
index 00000000..cadefed6
--- /dev/null
+++ b/gst/camerabin/camerabinpreview.h
@@ -0,0 +1,37 @@
+/*
+* GStreamer
+* Copyright (C) 2009 Nokia Corporation <multimedia@maemo.org>
+*
+* 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 __CAMERABINPREVIEW_H__
+#define __CAMERABINPREVIEW_H__
+
+#include <gst/gst.h>
+
+#include "gstcamerabin.h"
+
+G_BEGIN_DECLS
+ gboolean gst_camerabin_preview_create_pipeline (GstCameraBin * camera);
+
+void gst_camerabin_preview_destroy_pipeline (GstCameraBin * camera);
+
+GstBuffer *gst_camerabin_preview_convert (GstCameraBin * camera,
+ GstBuffer * buf);
+
+G_END_DECLS
+#endif /* __CAMERABINPREVIEW_H__ */
diff --git a/gst/camerabin/camerabinvideo.c b/gst/camerabin/camerabinvideo.c
index 14a95064..fef9ac26 100644
--- a/gst/camerabin/camerabinvideo.c
+++ b/gst/camerabin/camerabinvideo.c
@@ -290,6 +290,7 @@ gst_camerabin_video_change_state (GstElement * element,
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GstCameraBinVideo *vid = GST_CAMERABIN_VIDEO (element);
+ GstObject *camerabin = NULL;
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
@@ -325,6 +326,13 @@ gst_camerabin_video_change_state (GstElement * element,
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ camerabin = gst_element_get_parent (vid);
+ /* Write debug graph to file */
+ GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (camerabin),
+ GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE |
+ GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS, "videobin.playing");
+ gst_object_unref (camerabin);
+
if (vid->pending_eos) {
/* Video bin is still paused, so push eos directly to video queue */
GST_DEBUG_OBJECT (vid, "pushing pending eos");
@@ -641,7 +649,7 @@ gst_camerabin_video_create_elements (GstCameraBinVideo * vid)
}
/* Set queue leaky, we don't want to block video encoder feed, but
prefer leaking view finder buffers instead. */
- g_object_set (G_OBJECT (queue), "leaky", 2, NULL);
+ g_object_set (G_OBJECT (queue), "leaky", 2, "max-size-buffers", 1, NULL);
/* Set up src ghost pad for video bin */
vid_srcpad = gst_element_get_static_pad (queue, "src");
diff --git a/gst/camerabin/gstcamerabin.c b/gst/camerabin/gstcamerabin.c
index 2ddb34e0..f1cd897a 100644
--- a/gst/camerabin/gstcamerabin.c
+++ b/gst/camerabin/gstcamerabin.c
@@ -36,18 +36,19 @@
* <refsect2>
* <title>Example launch line</title>
* |[
- * gst-launch -v -m camerabin filename=test.jpeg
+ * gst-launch -v -m camerabin
* ]|
* </refsect2>
* <refsect2>
* <title>Image capture</title>
* <para>
* Taking still images is initiated with the #GstCameraBin::user-start action
- * signal. Once the image has captured, #GstCameraBin::img-done signal is fired.
- * It allows to decide wheter to take another picture (burst capture, bracketing
- * shot) or stop capturing. The last captured image is shown
- * until one switches back to view finder using #GstCameraBin::user-stop action
- * signal.
+ * signal. Once the image has been captured, "image-captured" gst message is
+ * posted to the bus and capturing another image is possible. If application
+ * has set #GstCameraBin:preview-caps property, then a "preview-image" gst
+ * message is posted to bus containing preview image formatted according to
+ * specified caps. Eventually when image has been saved #GstCameraBin::img-done
+ * signal is emitted.
*
* Available resolutions can be taken from the #GstCameraBin:inputcaps property.
* Image capture resolution can be set with #GstCameraBin::user-image-res
@@ -74,7 +75,8 @@
* of these settings require low-level support the photography interface support
* is dependent on video src element. In practice photography interface settings
* cannot be used successfully until in PAUSED state when the video src has
- * opened the video device.
+ * opened the video device. However it is possible to configure photography
+ * settings in NULL state and camerabin will try applying them later.
* </para>
* </refsect2>
* <refsect2>
@@ -105,16 +107,23 @@
/*
* The pipeline in the camerabin is
*
- * "image bin"
- * videosrc ! crop ! scale ! out-sel <------> in-sel ! scale ! ffmpegcsp ! vfsink
- * "video bin"
+ * videosrc [ ! ffmpegcsp ] ! capsfilter ! crop ! scale ! capsfilter ! \
+ * out-sel name=osel ! queue name=img_q
*
- * it is possible to have 'ffmpegcolorspace' and 'capsfilter' just after
- * v4l2camsrc
+ * View finder:
+ * osel. ! in-sel name=isel ! scale ! capsfilter [ ! ffmpegcsp ] ! vfsink
+ *
+ * Image bin:
+ * img_q. [ ! ipp ] ! ffmpegcsp ! imageenc ! metadatamux ! filesink
+ *
+ * Video bin:
+ * osel. ! tee name=t ! queue ! videoenc ! videomux name=mux ! filesink
+ * t. ! queue ! isel.
+ * audiosrc ! queue ! audioconvert ! volume ! audioenc ! mux.
*
* The properties of elements are:
*
- * vfsink - "sync", FALSE, "qos", FALSE
+ * vfsink - "sync", FALSE, "qos", FALSE, "async", FALSE
* output-selector - "resend-latest", FALSE
* input-selector - "select-all", TRUE
*/
@@ -134,11 +143,11 @@
/* FIXME: include #include <gst/gst-i18n-plugin.h> and use _(" ") */
#include "gstcamerabin.h"
-#include "gstcamerabinxoverlay.h"
#include "gstcamerabincolorbalance.h"
#include "gstcamerabinphotography.h"
#include "camerabingeneral.h"
+#include "camerabinpreview.h"
#include "gstcamerabin-marshal.h"
@@ -176,7 +185,8 @@ enum
ARG_VIDEO_SRC,
ARG_AUDIO_SRC,
ARG_INPUT_CAPS,
- ARG_FILTER_CAPS
+ ARG_FILTER_CAPS,
+ ARG_PREVIEW_CAPS
};
/*
@@ -216,6 +226,11 @@ static guint camerabin_signals[LAST_SIGNAL];
#define DEFAULT_VIEW_SINK "autovideosink"
+#define CAMERABIN_MAX_VF_WIDTH 848
+#define CAMERABIN_MAX_VF_HEIGHT 848
+#define PREVIEW_MESSAGE_NAME "preview-image"
+#define IMG_CAPTURED_MESSAGE_NAME "image-captured"
+
/*
* static helper functions declaration
*/
@@ -258,6 +273,12 @@ gst_camerabin_have_img_buffer (GstPad * pad, GstBuffer * buffer,
static gboolean
gst_camerabin_have_vid_buffer (GstPad * pad, GstBuffer * buffer,
gpointer u_data);
+static gboolean
+gst_camerabin_have_queue_data (GstPad * pad, GstMiniObject * mini_obj,
+ gpointer u_data);
+static gboolean
+gst_camerabin_have_src_buffer (GstPad * pad, GstBuffer * buffer,
+ gpointer u_data);
static void gst_camerabin_reset_to_view_finder (GstCameraBin * camera);
@@ -275,6 +296,8 @@ static const GValue *gst_camerabin_find_better_framerate (GstCameraBin * camera,
static void
gst_camerabin_update_aspect_filter (GstCameraBin * camera, GstCaps * new_caps);
+static void gst_camerabin_finish_image_capture (GstCameraBin * camera);
+
/*
* GObject callback functions declaration
*/
@@ -355,11 +378,7 @@ gst_camerabin_iface_supported (GstImplementsInterface * iface, GType iface_type)
{
GstCameraBin *camera = GST_CAMERABIN (iface);
- if (iface_type == GST_TYPE_X_OVERLAY) {
- if (camera->view_sink) {
- return GST_IS_X_OVERLAY (camera->view_sink);
- }
- } else if (iface_type == GST_TYPE_COLOR_BALANCE) {
+ if (iface_type == GST_TYPE_COLOR_BALANCE) {
if (camera->src_vid_src) {
return GST_IS_COLOR_BALANCE (camera->src_vid_src);
}
@@ -375,9 +394,8 @@ gst_camerabin_iface_supported (GstImplementsInterface * iface, GType iface_type)
return FALSE;
}
} else if (iface_type == GST_TYPE_PHOTOGRAPHY) {
- if (camera->src_vid_src) {
- return GST_IS_PHOTOGRAPHY (camera->src_vid_src);
- }
+ /* Always support photography interface */
+ return TRUE;
}
return FALSE;
@@ -402,12 +420,6 @@ camerabin_init_interfaces (GType type)
NULL,
};
- static const GInterfaceInfo camerabin_xoverlay_info = {
- (GInterfaceInitFunc) gst_camerabin_xoverlay_init,
- NULL,
- NULL,
- };
-
static const GInterfaceInfo camerabin_color_balance_info = {
(GInterfaceInitFunc) gst_camerabin_color_balance_init,
NULL,
@@ -428,9 +440,6 @@ camerabin_init_interfaces (GType type)
g_type_add_interface_static (type,
GST_TYPE_IMPLEMENTS_INTERFACE, &camerabin_info);
- g_type_add_interface_static (type, GST_TYPE_X_OVERLAY,
- &camerabin_xoverlay_info);
-
g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE,
&camerabin_color_balance_info);
@@ -470,6 +479,14 @@ camerabin_setup_src_elements (GstCameraBin * camera)
0));
}
+ /* Update photography interface settings */
+ if (GST_IS_ELEMENT (camera->src_vid_src) &&
+ gst_element_implements_interface (camera->src_vid_src,
+ GST_TYPE_PHOTOGRAPHY)) {
+ gst_photography_set_config (GST_PHOTOGRAPHY (camera->src_vid_src),
+ &camera->photo_settings);
+ }
+
if (camera->width > 0 && camera->height > 0) {
gst_structure_set (st,
"width", G_TYPE_INT, camera->width,
@@ -561,12 +578,6 @@ camerabin_create_src_elements (GstCameraBin * camera)
gst_camerabin_create_and_add_element (cbin, "output-selector")))
goto done;
- camera->srcpad_videosrc =
- gst_element_get_static_pad (camera->src_vid_src, "src");
-
- camera->srcpad_zoom_filter =
- gst_element_get_static_pad (camera->src_zoom_filter, "src");
-
/* Set default "driver-name" for v4l2camsrc if not set */
if (g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src_vid_src),
"driver-name")) {
@@ -637,14 +648,16 @@ camerabin_create_view_elements (GstCameraBin * camera)
&& (GST_PAD_DIRECTION (GST_PAD (pads->data)) != GST_PAD_SINK)) {
pads = g_list_next (pads);
}
- camera->pad_view_img = GST_PAD (pads->data);
+ camera->pad_view_src = GST_PAD (pads->data);
+ /* Add videoscale in case we need to downscale frame for view finder */
if (!(camera->view_scale =
gst_camerabin_create_and_add_element (GST_BIN (camera),
"videoscale"))) {
goto error;
}
+ /* Add capsfilter to maintain aspect ratio while scaling */
if (!(camera->aspect_filter =
gst_camerabin_create_and_add_element (GST_BIN (camera),
"capsfilter"))) {
@@ -694,30 +707,44 @@ camerabin_create_elements (GstCameraBin * camera)
goto done;
}
- /* Add image bin */
camera->pad_src_img =
gst_element_get_request_pad (camera->src_out_sel, "src%d");
- if (!gst_camerabin_add_element (GST_BIN (camera), camera->imgbin)) {
- goto done;
- }
+
gst_pad_add_buffer_probe (camera->pad_src_img,
G_CALLBACK (gst_camerabin_have_img_buffer), camera);
- /* Create view finder elements, this also links it to image bin */
- if (!camerabin_create_view_elements (camera)) {
- GST_WARNING_OBJECT (camera, "creating view failed");
+ /* Add image queue */
+ if (!(camera->img_queue =
+ gst_camerabin_create_and_add_element (GST_BIN (camera), "queue"))) {
+ goto done;
+ }
+
+ /* To avoid deadlock, we won't restrict the image queue size */
+ /* FIXME: actually we would like to have some kind of restriction here (size),
+ but deadlocks must be handled somehow... */
+ g_object_set (G_OBJECT (camera->img_queue), "max-size-time",
+ G_GUINT64_CONSTANT (0), NULL);
+ g_object_set (G_OBJECT (camera->img_queue), "max-size-bytes",
+ G_GUINT64_CONSTANT (0), NULL);
+ g_object_set (G_OBJECT (camera->img_queue), "max-size-buffers",
+ G_GUINT64_CONSTANT (0), NULL);
+
+ camera->pad_src_queue = gst_element_get_static_pad (camera->img_queue, "src");
+
+ gst_pad_add_data_probe (camera->pad_src_queue,
+ G_CALLBACK (gst_camerabin_have_queue_data), camera);
+
+ /* Add image bin */
+ if (!gst_camerabin_add_element (GST_BIN (camera), camera->imgbin)) {
goto done;
}
- /* Link output selector ! view_finder */
camera->pad_src_view =
gst_element_get_request_pad (camera->src_out_sel, "src%d");
- camera->pad_view_src =
- gst_element_get_request_pad (camera->view_in_sel, "sink%d");
- link_ret = gst_pad_link (camera->pad_src_view, camera->pad_view_src);
- if (GST_PAD_LINK_FAILED (link_ret)) {
- GST_ELEMENT_ERROR (camera, CORE, NEGOTIATION,
- ("linking view finder failed"), (NULL));
+
+ /* Create view finder elements */
+ if (!camerabin_create_view_elements (camera)) {
+ GST_WARNING_OBJECT (camera, "creating view finder elements failed");
goto done;
}
@@ -776,10 +803,6 @@ camerabin_destroy_elements (GstCameraBin * camera)
gst_element_release_request_pad (camera->src_out_sel, camera->pad_src_vid);
camera->pad_src_vid = NULL;
}
- if (camera->pad_view_img) {
- gst_element_release_request_pad (camera->view_in_sel, camera->pad_view_img);
- camera->pad_view_img = NULL;
- }
if (camera->pad_src_img) {
gst_element_release_request_pad (camera->src_out_sel, camera->pad_src_img);
camera->pad_src_img = NULL;
@@ -793,14 +816,9 @@ camerabin_destroy_elements (GstCameraBin * camera)
camera->pad_src_view = NULL;
}
- if (camera->srcpad_zoom_filter) {
- gst_object_unref (camera->srcpad_zoom_filter);
- camera->srcpad_zoom_filter = NULL;
- }
-
- if (camera->srcpad_videosrc) {
- gst_object_unref (camera->srcpad_videosrc);
- camera->srcpad_videosrc = NULL;
+ if (camera->pad_src_queue) {
+ gst_object_unref (camera->pad_src_queue);
+ camera->pad_src_queue = NULL;
}
camera->view_sink = NULL;
@@ -865,13 +883,23 @@ camerabin_dispose_elements (GstCameraBin * camera)
gst_caps_unref (camera->allowed_caps);
camera->allowed_caps = NULL;
}
+
+ if (camera->preview_caps) {
+ gst_caps_unref (camera->preview_caps);
+ camera->preview_caps = NULL;
+ }
+
+ if (camera->event_tags) {
+ gst_tag_list_free (camera->event_tags);
+ camera->event_tags = NULL;
+ }
}
/*
* gst_camerabin_image_capture_continue:
* @camera: camerabin object
*
- * Check if application wants to continue image capturing by using g_signal.
+ * Notify application that image has been saved with a signal.
*
* Returns TRUE if another image should be captured, FALSE otherwise.
*/
@@ -923,7 +951,16 @@ gst_camerabin_change_mode (GstCameraBin * camera, gint mode)
gst_element_set_state (camera->active_bin, GST_STATE_NULL);
}
if (camera->mode == MODE_IMAGE) {
+ GstStateChangeReturn state_ret;
+
camera->active_bin = camera->imgbin;
+ state_ret = gst_element_set_state (camera->active_bin, GST_STATE_READY);
+
+ if (state_ret == GST_STATE_CHANGE_FAILURE) {
+ GST_WARNING_OBJECT (camera, "state change failed");
+ gst_element_set_state (camera->active_bin, GST_STATE_NULL);
+ camera->active_bin = NULL;
+ }
} else if (camera->mode == MODE_VIDEO) {
camera->active_bin = camera->vidbin;
}
@@ -1114,6 +1151,46 @@ failed:
}
/*
+ * gst_camerabin_send_img_queue_event:
+ * @camera: camerabin object
+ * @event: event to be sent
+ *
+ * Send the given event to image queue.
+ */
+static void
+gst_camerabin_send_img_queue_event (GstCameraBin * camera, GstEvent * event)
+{
+ GstPad *queue_sink;
+
+ g_return_if_fail (camera != NULL);
+ g_return_if_fail (event != NULL);
+
+ queue_sink = gst_element_get_static_pad (camera->img_queue, "sink");
+ gst_pad_send_event (queue_sink, event);
+ gst_object_unref (queue_sink);
+}
+
+/*
+ * gst_camerabin_send_img_queue_custom_event:
+ * @camera: camerabin object
+ * @ev_struct: event structure to be sent
+ *
+ * Generate and send a custom event to image queue.
+ */
+static void
+gst_camerabin_send_img_queue_custom_event (GstCameraBin * camera,
+ GstStructure * ev_struct)
+{
+ GstEvent *event;
+
+ g_return_if_fail (camera != NULL);
+ g_return_if_fail (ev_struct != NULL);
+
+ event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, ev_struct);
+ gst_camerabin_send_img_queue_event (camera, event);
+}
+
+/*
* gst_camerabin_rewrite_tags_to_bin:
* @bin: bin holding tag setter elements
* @list: tag list to be written
@@ -1275,7 +1352,13 @@ gst_camerabin_rewrite_tags (GstCameraBin * camera)
}
/* Write tags */
- gst_camerabin_rewrite_tags_to_bin (GST_BIN (camera->active_bin), list);
+ if (camera->active_bin == camera->vidbin) {
+ gst_camerabin_rewrite_tags_to_bin (GST_BIN (camera->active_bin), list);
+ } else {
+ /* Image tags need to be sent as a serialized event into image queue */
+ GstEvent *tagevent = gst_event_new_tag (gst_tag_list_copy (list));
+ gst_camerabin_send_img_queue_event (camera, tagevent);
+ }
gst_tag_list_free (list);
}
@@ -1313,18 +1396,36 @@ gst_camerabin_set_capsfilter_caps (GstCameraBin * camera, GstCaps * new_caps)
gst_camerabin_update_aspect_filter (camera, new_caps);
}
+/*
+ * gst_camerabin_adapt_video_resolution:
+ * @camera: camerabin object
+ * @caps: caps describing the next incoming buffer format
+ *
+ * This function adjusts capsfilter and crop elements in order to modify
+ * the incoming buffer to the resolution that application requested.
+ *
+ */
static void
gst_camerabin_adapt_video_resolution (GstCameraBin * camera, GstCaps * caps)
{
GstStructure *st;
gint width = 0, height = 0;
GstCaps *filter_caps = NULL;
+ gint top, bottom, left, right, crop;
+ gdouble ratio_w, ratio_h;
+
+ g_return_if_fail (camera->width != 0 && camera->height != 0);
/* Get width and height from caps */
st = gst_caps_get_structure (caps, 0);
gst_structure_get_int (st, "width", &width);
gst_structure_get_int (st, "height", &height);
+ if (width == camera->width && height == camera->height) {
+ GST_DEBUG_OBJECT (camera, "no adaptation with resolution needed");
+ return;
+ }
+
GST_DEBUG_OBJECT (camera,
"changing %dx%d -> %dx%d filter to %" GST_PTR_FORMAT,
camera->width, camera->height, width, height, camera->src_filter);
@@ -1336,7 +1437,31 @@ gst_camerabin_adapt_video_resolution (GstCameraBin * camera, GstCaps * caps)
"height", G_TYPE_INT, height, NULL);
g_object_set (G_OBJECT (camera->src_filter), "caps", filter_caps, NULL);
gst_caps_unref (filter_caps);
- /* FIXME: implement cropping according to requested aspect ratio */
+
+ /* Crop if requested aspect ratio differs from incoming frame aspect ratio */
+
+ /* Don't override original crop values in case we have zoom applied */
+ g_object_get (G_OBJECT (camera->src_zoom_crop), "top", &top, "bottom",
+ &bottom, "left", &left, "right", &right, NULL);
+
+ ratio_w = (gdouble) width / camera->width;
+ ratio_h = (gdouble) height / camera->height;
+
+ if (ratio_w < ratio_h) {
+ crop = height - (camera->height * ratio_w);
+ top += crop / 2;
+ bottom += crop / 2;
+ } else {
+ crop = width - (camera->width * ratio_h);
+ left += crop / 2;
+ right += crop / 2;
+ }
+
+ GST_INFO_OBJECT (camera,
+ "updating crop: left:%d, right:%d, top:%d, bottom:%d", left, right, top,
+ bottom);
+ g_object_set (G_OBJECT (camera->src_zoom_crop), "top", top, "bottom", bottom,
+ "left", left, "right", right, NULL);
}
/*
@@ -1353,11 +1478,13 @@ img_capture_prepared (gpointer data, GstCaps * caps)
GstStructure *st, *new_st;
gint i;
const gchar *field_name;
+ gboolean adapt = FALSE;
GST_INFO_OBJECT (camera, "image capture prepared");
/* It is possible we are about to get something else that we requested */
if (!gst_caps_is_equal (camera->image_capture_caps, caps)) {
+ adapt = TRUE;
/* If capture preparation has added new fields to requested caps,
we need to copy them */
st = gst_caps_get_structure (camera->image_capture_caps, 0);
@@ -1378,14 +1505,17 @@ img_capture_prepared (gpointer data, GstCaps * caps)
/* Update capsfilters */
gst_camerabin_set_capsfilter_caps (camera, camera->image_capture_caps);
- /* If incoming buffer resolution is different from what application
- requested, then we need to fix this in camerabin */
- gst_camerabin_adapt_video_resolution (camera, caps);
-
+ if (adapt) {
+ /* If incoming buffer resolution is different from what application
+ requested, then we can fix this in camerabin */
+ gst_camerabin_adapt_video_resolution (camera, caps);
+ }
g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", FALSE,
"active-pad", camera->pad_src_img, NULL);
- gst_camerabin_rewrite_tags (camera);
- gst_element_set_state (GST_ELEMENT (camera->imgbin), GST_STATE_PLAYING);
+
+ if (!GST_CAMERABIN_IMAGE (camera->imgbin)->elements_created) {
+ gst_element_set_state (camera->imgbin, GST_STATE_READY);
+ }
}
/*
@@ -1431,8 +1561,11 @@ gst_camerabin_start_image_capture (GstCameraBin * camera)
}
if (!wait_for_prepare) {
- gst_camerabin_rewrite_tags (camera);
- state_ret = gst_element_set_state (camera->imgbin, GST_STATE_PLAYING);
+ /* Image queue's srcpad data probe will set imagebin to PLAYING */
+ state_ret = gst_element_set_state (camera->imgbin, GST_STATE_PAUSED);
+ GST_DEBUG_OBJECT (camera, "setting imagebin to paused: %s",
+ gst_element_state_change_return_get_name (state_ret));
+
if (state_ret != GST_STATE_CHANGE_FAILURE) {
g_mutex_lock (camera->capture_mutex);
g_object_set (G_OBJECT (camera->src_out_sel), "resend-latest", TRUE,
@@ -1535,12 +1668,48 @@ image_pad_blocked (GstPad * pad, gboolean blocked, gpointer user_data)
GST_DEBUG_OBJECT (camera, "%s %s:%s",
blocked ? "blocking" : "unblocking", GST_DEBUG_PAD_NAME (pad));
+}
+
+/*
+ * gst_camerabin_send_preview:
+ * @camera: camerabin object
+ * @buffer: received buffer
+ *
+ * Convert given buffer to desired preview format and send is as a #GstMessage
+ * to application.
+ *
+ * Returns: TRUE always
+ */
+static gboolean
+gst_camerabin_send_preview (GstCameraBin * camera, GstBuffer * buffer)
+{
+ GstBuffer *prev = NULL;
+ GstStructure *s;
+ GstMessage *msg;
+ gboolean ret = FALSE;
+
+ GST_DEBUG_OBJECT (camera, "creating preview");
+
+ prev = gst_camerabin_preview_convert (camera, buffer);
- if (blocked && (pad == camera->srcpad_videosrc)) {
- /* Send eos and block until image bin reaches eos */
- GST_DEBUG_OBJECT (camera, "sending eos to image bin");
- gst_element_send_event (camera->imgbin, gst_event_new_eos ());
+ GST_DEBUG_OBJECT (camera, "preview created: %p", prev);
+
+ if (prev) {
+ s = gst_structure_new (PREVIEW_MESSAGE_NAME,
+ "buffer", GST_TYPE_BUFFER, prev, NULL);
+
+ msg = gst_message_new_element (GST_OBJECT (camera), s);
+
+ GST_DEBUG_OBJECT (camera, "sending message with preview image");
+
+ if (gst_element_post_message (GST_ELEMENT (camera), msg) == FALSE) {
+ GST_WARNING_OBJECT (camera,
+ "This element has no bus, therefore no message sent!");
+ }
+ ret = TRUE;
}
+
+ return ret;
}
/*
@@ -1549,27 +1718,19 @@ image_pad_blocked (GstPad * pad, gboolean blocked, gpointer user_data)
* @buffer: still image frame
* @u_data: camera bin object
*
- * Buffer probe called before sending each buffer to image bin.
- *
- * First buffer is always passed directly to image bin. Then pad
- * is blocked in order to interleave buffers with eos events.
- * Interleaving eos events and buffers is needed when we have
- * decoupled elements in the image bin capture pipeline.
- * After image bin posts eos message, then pad is unblocked.
- * Next, image bin is changed to READY state in order to save the
- * file and the application is allowed to decide whether to
- * continue image capture. If yes, only then the next buffer is
- * passed to image bin.
+ * Buffer probe called before sending each buffer to image queue.
+ * Generates and sends preview image as gst message if requested.
*/
static gboolean
gst_camerabin_have_img_buffer (GstPad * pad, GstBuffer * buffer,
gpointer u_data)
{
GstCameraBin *camera = (GstCameraBin *) u_data;
+ GstStructure *fn_ev_struct = NULL;
gboolean ret = TRUE;
+ GstPad *os_sink = NULL;
- GST_LOG ("got buffer #%d %p with size %d", camera->num_img_buffers,
- buffer, GST_BUFFER_SIZE (buffer));
+ GST_LOG ("got buffer %p with size %d", buffer, GST_BUFFER_SIZE (buffer));
/* Image filename should be set by now */
if (g_str_equal (camera->filename->str, "")) {
@@ -1578,54 +1739,38 @@ gst_camerabin_have_img_buffer (GstPad * pad, GstBuffer * buffer,
goto done;
}
- /* Check for first buffer after capture start, we want to
- pass it forward directly. */
- if (!camera->num_img_buffers) {
- goto done;
+ if (camera->preview_caps) {
+ gst_camerabin_send_preview (camera, buffer);
}
- /* Close the file of saved image */
- gst_element_set_state (camera->imgbin, GST_STATE_READY);
-
- /* Reset filename to force application set new filename */
- g_string_assign (camera->filename, "");
-
- /* Check if the application wants to continue */
- ret = gst_camerabin_image_capture_continue (camera);
+ gst_camerabin_rewrite_tags (camera);
- if (ret && !camera->stop_requested) {
- GST_DEBUG_OBJECT (camera, "capturing image \"%s\"", camera->filename->str);
- g_object_set (G_OBJECT (camera->imgbin), "filename",
- camera->filename->str, NULL);
- gst_element_set_state (camera->imgbin, GST_STATE_PLAYING);
- } else {
- GST_DEBUG_OBJECT (camera, "not continuing (cont:%d, stop_req:%d)",
- ret, camera->stop_requested);
+ /* Send a custom event which tells the filename to image queue */
+ /* NOTE: This needs to be THE FIRST event to be sent to queue for
+ every image. It triggers imgbin state change to PLAYING. */
+ fn_ev_struct = gst_structure_new ("img-filename",
+ "filename", G_TYPE_STRING, camera->filename->str, NULL);
+ GST_DEBUG_OBJECT (camera, "sending filename event to image queue");
+ gst_camerabin_send_img_queue_custom_event (camera, fn_ev_struct);
+
+ /* Add buffer probe to outputselector's sink pad. It sends
+ EOS event to image queue. */
+ os_sink = gst_element_get_static_pad (camera->src_out_sel, "sink");
+ camera->image_captured_id = gst_pad_add_buffer_probe (os_sink,
+ G_CALLBACK (gst_camerabin_have_src_buffer), camera);
+ gst_object_unref (os_sink);
- /* Block dataflow to the output-selector to show preview image in
- view finder. Continue and unblock when capture is stopped */
- gst_pad_set_blocked_async (camera->srcpad_zoom_filter, TRUE,
- (GstPadBlockCallback) image_pad_blocked, camera);
- ret = FALSE; /* Drop the buffer */
+done:
- g_mutex_lock (camera->capture_mutex);
- camera->capturing = FALSE;
- g_cond_signal (camera->cond);
- g_mutex_unlock (camera->capture_mutex);
- }
+ /* HACK: v4l2camsrc changes to view finder resolution automatically
+ after one captured still image */
+ gst_camerabin_finish_image_capture (camera);
-done:
+ gst_camerabin_reset_to_view_finder (camera);
- if (ret) {
- camera->num_img_buffers++;
- /* Block when next buffer arrives, we want to push eos event
- between frames and make sure that eos reaches the filesink
- before processing the next buffer. */
- gst_pad_set_blocked_async (camera->srcpad_videosrc, TRUE,
- (GstPadBlockCallback) image_pad_blocked, camera);
- }
+ GST_DEBUG_OBJECT (camera, "switched back to viewfinder");
- return ret;
+ return TRUE;
}
/*
@@ -1655,6 +1800,120 @@ gst_camerabin_have_vid_buffer (GstPad * pad, GstBuffer * buffer,
}
/*
+ * gst_camerabin_have_src_buffer:
+ * @pad: output-selector sink pad which receives frames from video source
+ * @buffer: buffer pushed to the pad
+ * @u_data: camerabin object
+ *
+ * Buffer probe for sink pad. It sends custom eos event to image queue and
+ * notifies application by sending a "image-captured" message to GstBus.
+ * This probe is installed after image has been captured and it disconnects
+ * itself after EOS has been sent.
+ */
+static gboolean
+gst_camerabin_have_src_buffer (GstPad * pad, GstBuffer * buffer,
+ gpointer u_data)
+{
+ GstCameraBin *camera = (GstCameraBin *) u_data;
+ GstMessage *msg;
+
+ GST_LOG_OBJECT (camera, "got image buffer %p with size %d",
+ buffer, GST_BUFFER_SIZE (buffer));
+
+ /* We can't send real EOS event, since it would switch the image queue
+ into "draining mode". Therefore we send our own custom eos and
+ catch & drop it later in queue's srcpad data probe */
+ GST_DEBUG_OBJECT (camera, "sending eos to image queue");
+ gst_camerabin_send_img_queue_custom_event (camera,
+ gst_structure_new ("img-eos", NULL));
+
+ /* our work is done, disconnect */
+ gst_pad_remove_buffer_probe (pad, camera->image_captured_id);
+
+ g_mutex_lock (camera->capture_mutex);
+ camera->capturing = FALSE;
+ g_cond_signal (camera->cond);
+ g_mutex_unlock (camera->capture_mutex);
+
+ msg = gst_message_new_element (GST_OBJECT (camera),
+ gst_structure_new (IMG_CAPTURED_MESSAGE_NAME, NULL));
+
+ GST_DEBUG_OBJECT (camera, "sending 'image captured' message");
+
+ if (gst_element_post_message (GST_ELEMENT (camera), msg) == FALSE) {
+ GST_WARNING_OBJECT (camera,
+ "This element has no bus, therefore no message sent!");
+ }
+
+ return TRUE;
+}
+
+/*
+ * gst_camerabin_have_queue_data:
+ * @pad: image queue src pad leading to image bin
+ * @mini_obj: buffer or event pushed to the pad
+ * @u_data: camerabin object
+ *
+ * Buffer probe for image queue src pad leading to image bin. It sets imgbin
+ * into PLAYING mode when image buffer is passed to it. This probe also
+ * monitors our internal custom events and handles them accordingly.
+ */
+static gboolean
+gst_camerabin_have_queue_data (GstPad * pad, GstMiniObject * mini_obj,
+ gpointer u_data)
+{
+ GstCameraBin *camera = (GstCameraBin *) u_data;
+ gboolean ret = TRUE;
+
+ if (GST_IS_BUFFER (mini_obj)) {
+ GstEvent *tagevent;
+
+ GST_LOG_OBJECT (camera, "queue sending image buffer to imgbin");
+
+ tagevent = gst_event_new_tag (gst_tag_list_copy (camera->event_tags));
+ gst_element_send_event (camera->imgbin, tagevent);
+ gst_tag_list_free (camera->event_tags);
+ camera->event_tags = gst_tag_list_new ();
+ } else if (GST_IS_EVENT (mini_obj)) {
+ const GstStructure *evs;
+ GstEvent *event;
+
+ event = GST_EVENT_CAST (mini_obj);
+ evs = gst_event_get_structure (event);
+
+ GST_LOG_OBJECT (camera, "got event %s", GST_EVENT_TYPE_NAME (event));
+
+ if (GST_EVENT_TYPE (event) == GST_EVENT_TAG) {
+ GstTagList *tlist;
+
+ gst_event_parse_tag (event, &tlist);
+ gst_tag_list_insert (camera->event_tags, tlist, GST_TAG_MERGE_REPLACE);
+ ret = FALSE;
+ } else if (evs && gst_structure_has_name (evs, "img-filename")) {
+ const gchar *fname;
+
+ GST_LOG_OBJECT (camera, "queue setting image filename to imagebin");
+ fname = gst_structure_get_string (evs, "filename");
+ g_object_set (G_OBJECT (camera->imgbin), "filename", fname, NULL);
+
+ /* imgbin fails to start unless the filename is set */
+ gst_element_set_state (camera->imgbin, GST_STATE_PLAYING);
+ GST_LOG_OBJECT (camera, "Set imgbin to PLAYING");
+
+ ret = FALSE;
+ } else if (evs && gst_structure_has_name (evs, "img-eos")) {
+ GST_LOG_OBJECT (camera, "queue sending EOS to image pipeline");
+ gst_pad_set_blocked_async (camera->pad_src_queue, TRUE,
+ (GstPadBlockCallback) image_pad_blocked, camera);
+ gst_element_send_event (camera->imgbin, gst_event_new_eos ());
+ ret = FALSE;
+ }
+ }
+
+ return ret;
+}
+
+/*
* gst_camerabin_reset_to_view_finder:
* @camera: camerabin object
*
@@ -1667,8 +1926,8 @@ gst_camerabin_reset_to_view_finder (GstCameraBin * camera)
GstStateChangeReturn state_ret;
GST_DEBUG_OBJECT (camera, "resetting");
- /* Set active bin to READY state */
- if (camera->active_bin) {
+ /* Set video bin to READY state */
+ if (camera->active_bin == camera->vidbin) {
state_ret = gst_element_set_state (camera->active_bin, GST_STATE_READY);
if (state_ret == GST_STATE_CHANGE_FAILURE) {
GST_WARNING_OBJECT (camera, "state change failed");
@@ -1678,7 +1937,6 @@ gst_camerabin_reset_to_view_finder (GstCameraBin * camera)
}
/* Reset counters and flags */
- camera->num_img_buffers = 0;
camera->stop_requested = FALSE;
camera->paused = FALSE;
@@ -1688,18 +1946,6 @@ gst_camerabin_reset_to_view_finder (GstCameraBin * camera)
"active-pad", camera->pad_src_view, NULL);
}
- /* Unblock, if dataflow to output-selector is blocked due to image preview */
- if (camera->srcpad_zoom_filter &&
- gst_pad_is_blocked (camera->srcpad_zoom_filter)) {
- gst_pad_set_blocked_async (camera->srcpad_zoom_filter, FALSE,
- (GstPadBlockCallback) image_pad_blocked, camera);
- }
- /* Unblock, if dataflow in videosrc is blocked due to waiting for eos */
- if (camera->srcpad_videosrc && gst_pad_is_blocked (camera->srcpad_videosrc)) {
- gst_pad_set_blocked_async (camera->srcpad_videosrc, FALSE,
- (GstPadBlockCallback) image_pad_blocked, camera);
- }
-
/* Enable view finder mode in v4l2camsrc */
if (camera->src_vid_src &&
g_object_class_find_property (G_OBJECT_GET_CLASS (camera->src_vid_src),
@@ -2023,6 +2269,26 @@ gst_camerabin_update_aspect_filter (GstCameraBin * camera, GstCaps * new_caps)
}
/*
+ * gst_camerabin_finish_image_capture:
+ * @camera: camerabin object
+ *
+ * Perform finishing operations after image capture is done and
+ * returning back to view finder mode.
+ */
+static void
+gst_camerabin_finish_image_capture (GstCameraBin * camera)
+{
+ if (camera->image_capture_caps) {
+ /* If we used specific caps for image capture we need to
+ restore the caps and zoom/crop for view finder mode */
+ GST_DEBUG_OBJECT (camera, "resetting crop in camerabin");
+ g_object_set (camera->src_zoom_crop, "left", 0, "right", 0,
+ "top", 0, "bottom", 0, NULL);
+ gst_camerabin_set_capsfilter_caps (camera, camera->view_finder_caps);
+ }
+}
+
+/*
* GObject callback functions implementation
*/
@@ -2243,14 +2509,29 @@ gst_camerabin_class_init (GstCameraBinClass * klass)
/**
* GstCameraBin:filter-caps:
*
- * Filter video source element caps using this property.
- * This is an alternative to #GstCamerabin::user-res-fps action
- * signal that allows more fine grained control of video source.
+ * Caps applied to capsfilter element after videosrc [ ! ffmpegcsp ].
+ * You can use this e.g. to make sure video color format matches with
+ * encoders and other elements configured to camerabin and/or change
+ * resolution and frame rate.
*/
g_object_class_install_property (gobject_class, ARG_FILTER_CAPS,
g_param_spec_boxed ("filter-caps", "Filter caps",
- "Capsfilter caps used to control video source operation",
+ "Filter video data coming from videosrc element",
+ GST_TYPE_CAPS, G_PARAM_READWRITE));
+
+ /**
+ * GstCameraBin:preview-caps:
+ *
+ * If application wants to receive a preview image, it needs to
+ * set this property to depict the desired image format caps. When
+ * this property is not set (NULL), message containing the preview
+ * image is not sent.
+ */
+
+ g_object_class_install_property (gobject_class, ARG_PREVIEW_CAPS,
+ g_param_spec_boxed ("preview-caps", "Preview caps",
+ "Caps defining the preview image format",
GST_TYPE_CAPS, G_PARAM_READWRITE));
/**
@@ -2347,9 +2628,7 @@ gst_camerabin_class_init (GstCameraBinClass * klass)
* @camera: the camera bin element
* @filename: the name of the file just saved
*
- * Signal emitted when the file has just been saved. To continue taking
- * pictures set new filename using #GstCameraBin:filename property and return
- * TRUE, otherwise return FALSE.
+ * Signal emitted when the file has just been saved.
*
* Don't call any #GstCameraBin method from this signal, if you do so there
* will be a deadlock.
@@ -2394,7 +2673,6 @@ gst_camerabin_init (GstCameraBin * camera, GstCameraBinClass * gclass)
camera->filename = g_string_new ("");
camera->mode = DEFAULT_MODE;
- camera->num_img_buffers = 0;
camera->stop_requested = FALSE;
camera->paused = FALSE;
camera->capturing = FALSE;
@@ -2405,6 +2683,8 @@ gst_camerabin_init (GstCameraBin * camera, GstCameraBinClass * gclass)
camera->fps_n = DEFAULT_FPS_N;
camera->fps_d = DEFAULT_FPS_D;
+ camera->event_tags = gst_tag_list_new ();
+
camera->image_capture_caps = NULL;
camera->view_finder_caps = NULL;
camera->allowed_caps = NULL;
@@ -2419,13 +2699,9 @@ gst_camerabin_init (GstCameraBin * camera, GstCameraBinClass * gclass)
camera->pad_src_view = NULL;
camera->pad_view_src = NULL;
camera->pad_src_img = NULL;
- camera->pad_view_img = NULL;
camera->pad_src_vid = NULL;
camera->pad_view_vid = NULL;
- camera->srcpad_zoom_filter = NULL;
- camera->srcpad_videosrc = NULL;
-
/* source elements */
camera->src_vid_src = NULL;
camera->src_filter = NULL;
@@ -2450,6 +2726,8 @@ gst_camerabin_init (GstCameraBin * camera, GstCameraBinClass * gclass)
camera->view_in_sel = NULL;
camera->view_scale = NULL;
camera->view_sink = NULL;
+
+ memset (&camera->photo_settings, 0, sizeof (GstPhotoSettings));
}
static void
@@ -2467,6 +2745,8 @@ gst_camerabin_dispose (GObject * object)
gst_element_set_state (camera->vidbin, GST_STATE_NULL);
gst_object_unref (camera->vidbin);
+ gst_camerabin_preview_destroy_pipeline (camera);
+
camerabin_destroy_elements (camera);
camerabin_dispose_elements (camera);
@@ -2527,7 +2807,7 @@ gst_camerabin_set_property (GObject * object, guint prop_id,
break;
case ARG_VIDEO_MUX:
if (GST_STATE (camera->vidbin) != GST_STATE_NULL) {
- GST_WARNING_OBJECT (camera->vidbin,
+ GST_WARNING_OBJECT (camera,
"can't use set element until next video bin NULL to READY state change");
}
gst_camerabin_video_set_muxer (GST_CAMERABIN_VIDEO (camera->vidbin),
@@ -2588,7 +2868,18 @@ gst_camerabin_set_property (GObject * object, guint prop_id,
}
camera->view_finder_caps = gst_caps_copy (gst_value_get_caps (value));
GST_OBJECT_UNLOCK (camera);
- gst_camerabin_set_capsfilter_caps (camera, camera->view_finder_caps);
+ if (GST_STATE (camera) != GST_STATE_NULL) {
+ gst_camerabin_set_capsfilter_caps (camera, camera->view_finder_caps);
+ }
+ break;
+ case ARG_PREVIEW_CAPS:
+ GST_OBJECT_LOCK (camera);
+ if (camera->preview_caps) {
+ gst_caps_unref (camera->preview_caps);
+ }
+ camera->preview_caps = gst_caps_copy (gst_value_get_caps (value));
+ GST_OBJECT_UNLOCK (camera);
+ gst_camerabin_preview_create_pipeline (camera);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -2661,6 +2952,9 @@ gst_camerabin_get_property (GObject * object, guint prop_id,
case ARG_FILTER_CAPS:
gst_value_set_caps (value, camera->view_finder_caps);
break;
+ case ARG_PREVIEW_CAPS:
+ gst_value_set_caps (value, camera->preview_caps);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -2732,6 +3026,29 @@ done:
return ret;
}
+static gboolean
+gst_camerabin_imgbin_finished (gpointer u_data)
+{
+ GstCameraBin *camera = GST_CAMERABIN (u_data);
+
+ GST_DEBUG_OBJECT (camera, "Image encoding finished");
+
+ /* Close the file of saved image */
+ gst_element_set_state (camera->imgbin, GST_STATE_READY);
+ GST_DEBUG_OBJECT (camera, "Image pipeline set to READY");
+
+ /* Send img-done signal */
+ gst_camerabin_image_capture_continue (camera);
+
+ /* Unblock image queue pad to process next buffer */
+ gst_pad_set_blocked_async (camera->pad_src_queue, FALSE,
+ (GstPadBlockCallback) image_pad_blocked, camera);
+ GST_DEBUG_OBJECT (camera, "Queue srcpad unblocked");
+
+ /* disconnect automatically */
+ return FALSE;
+}
+
/*
* GstBin functions implementation
*/
@@ -2755,17 +3072,20 @@ gst_camerabin_handle_message_func (GstBin * bin, GstMessage * msg)
} else if (GST_MESSAGE_SRC (msg) == GST_OBJECT (camera->imgbin)) {
/* Image eos */
GST_DEBUG_OBJECT (camera, "got image eos message");
-
- /* Still image capture buffer handled, restore filter caps */
- if (camera->image_capture_caps) {
- gst_camerabin_set_capsfilter_caps (camera, camera->view_finder_caps);
- }
-
- /* Unblock pad to process next buffer */
- gst_pad_set_blocked_async (camera->srcpad_videosrc, FALSE,
- (GstPadBlockCallback) image_pad_blocked, camera);
+ g_idle_add (gst_camerabin_imgbin_finished, camera);
}
break;
+ case GST_MESSAGE_ERROR:
+ GST_DEBUG_OBJECT (camera, "error from child %" GST_PTR_FORMAT,
+ GST_MESSAGE_SRC (msg));
+ g_mutex_lock (camera->capture_mutex);
+ if (camera->capturing) {
+ gst_camerabin_finish_image_capture (camera);
+ camera->capturing = FALSE;
+ g_cond_signal (camera->cond);
+ }
+ g_mutex_unlock (camera->capture_mutex);
+ break;
default:
break;
}
@@ -2807,12 +3127,11 @@ gst_camerabin_user_start (GstCameraBin * camera)
g_mutex_unlock (camera->capture_mutex);
if (camera->active_bin) {
- g_object_set (G_OBJECT (camera->active_bin), "filename",
- camera->filename->str, NULL);
-
if (camera->active_bin == camera->imgbin) {
gst_camerabin_start_image_capture (camera);
} else if (camera->active_bin == camera->vidbin) {
+ g_object_set (G_OBJECT (camera->active_bin), "filename",
+ camera->filename->str, NULL);
gst_camerabin_start_video_recording (camera);
}
}
@@ -2821,10 +3140,13 @@ gst_camerabin_user_start (GstCameraBin * camera)
static void
gst_camerabin_user_stop (GstCameraBin * camera)
{
- GST_INFO_OBJECT (camera, "stopping %s capture",
- camera->mode ? "video" : "image");
- gst_camerabin_do_stop (camera);
- gst_camerabin_reset_to_view_finder (camera);
+ if (camera->active_bin == camera->vidbin) {
+ GST_INFO_OBJECT (camera, "stopping video capture");
+ gst_camerabin_do_stop (camera);
+ gst_camerabin_reset_to_view_finder (camera);
+ } else {
+ GST_INFO_OBJECT (camera, "stopping image capture isn't needed");
+ }
}
static void
@@ -2891,6 +3213,9 @@ gst_camerabin_user_res_fps (GstCameraBin * camera, gint width, gint height,
GST_INFO_OBJECT (camera, "switching resolution to %dx%d and fps to %d/%d",
width, height, fps_n, fps_d);
+ /* Interrupt ongoing capture */
+ gst_camerabin_do_stop (camera);
+
gst_element_get_state (GST_ELEMENT (camera), &state, &pending, 0);
if (state == GST_STATE_PAUSED || state == GST_STATE_PLAYING) {
GST_INFO_OBJECT (camera,
diff --git a/gst/camerabin/gstcamerabin.h b/gst/camerabin/gstcamerabin.h
index 1bb3f489..cd88c7ca 100644
--- a/gst/camerabin/gstcamerabin.h
+++ b/gst/camerabin/gstcamerabin.h
@@ -59,7 +59,6 @@ struct _GstCameraBin
/* private */
GString *filename;
gint mode; /* MODE_IMAGE or MODE_VIDEO */
- guint num_img_buffers; /* no of image buffers captured */
gboolean stop_requested; /* TRUE if capturing stop needed */
gboolean paused; /* TRUE if capturing paused */
@@ -69,6 +68,9 @@ struct _GstCameraBin
gint fps_n;
gint fps_d;
+ /* Image tags are collected here first before sending to imgbin */
+ GstTagList *event_tags;
+
/* Caps applied to capsfilters when taking still image */
GstCaps *image_capture_caps;
@@ -78,6 +80,9 @@ struct _GstCameraBin
/* Caps that videosrc supports */
GstCaps *allowed_caps;
+ /* Caps used to create preview image */
+ GstCaps *preview_caps;
+
/* The digital zoom (from 100% to 1000%) */
gint zoom;
@@ -90,16 +95,16 @@ struct _GstCameraBin
GstPad *pad_src_view;
GstPad *pad_view_src;
GstPad *pad_src_img;
- GstPad *pad_view_img;
GstPad *pad_src_vid;
GstPad *pad_view_vid;
+ GstPad *pad_src_queue;
- GstPad *srcpad_zoom_filter;
- GstPad *srcpad_videosrc;
-
+ GstElement *img_queue; /* queue for decoupling capture from
+ image-postprocessing and saving */
GstElement *imgbin; /* bin that holds image capturing elements */
GstElement *vidbin; /* bin that holds video capturing elements */
GstElement *active_bin; /* image or video bin that is currently in use */
+ GstElement *preview_pipeline; /* pipeline for creating preview images */
/* source elements */
GstElement *src_vid_src;
@@ -123,6 +128,12 @@ struct _GstCameraBin
gboolean night_mode;
gint pre_night_fps_n;
gint pre_night_fps_d;
+
+ /* Cache the photography interface settings */
+ GstPhotoSettings photo_settings;
+
+ /* Buffer probe id for captured image handling */
+ gulong image_captured_id;
};
/**
@@ -145,7 +156,7 @@ struct _GstCameraBinClass
/* signals (callback) */
- gboolean (*img_done) (GstCameraBin * camera, const gchar * filename);
+ gboolean (*img_done) (GstCameraBin * camera, const gchar * filename);
};
/**
@@ -164,4 +175,4 @@ typedef enum
GType gst_camerabin_get_type (void);
G_END_DECLS
-#endif /* #ifndef __GST_CAMERABIN_H__ */
+#endif /* #ifndef __GST_CAMERABIN_H__ */
diff --git a/gst/camerabin/gstcamerabinphotography.c b/gst/camerabin/gstcamerabinphotography.c
index 8a95bcd7..cb17abca 100644
--- a/gst/camerabin/gstcamerabinphotography.c
+++ b/gst/camerabin/gstcamerabinphotography.c
@@ -24,6 +24,7 @@
#include <config.h>
#endif
+#include <string.h>
#include "gstcamerabinphotography.h"
#include "gstcamerabin.h"
@@ -32,38 +33,206 @@ GST_DEBUG_CATEGORY_STATIC (camerabinphoto_debug);
#define PHOTOGRAPHY_IS_OK(photo_elem) (GST_IS_ELEMENT (photo_elem) && \
gst_element_implements_interface (photo_elem, GST_TYPE_PHOTOGRAPHY))
+static void
+gst_camerabin_handle_scene_mode (GstCameraBin * camera,
+ GstSceneMode scene_mode);
+
+static gboolean
+gst_camerabin_set_ev_compensation (GstPhotography * photo,
+ gfloat ev_compensation)
+{
+ GstCameraBin *camera;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (photo != NULL, FALSE);
+
+ camera = GST_CAMERABIN (photo);
+
+ /* Cache the setting */
+ camera->photo_settings.ev_compensation = ev_compensation;
+
+ if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+ ret =
+ gst_photography_set_ev_compensation (GST_PHOTOGRAPHY
+ (camera->src_vid_src), ev_compensation);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_get_ev_compensation (GstPhotography * photo,
+ gfloat * ev_compensation)
+{
+ GstCameraBin *camera;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (photo != NULL, FALSE);
+
+ camera = GST_CAMERABIN (photo);
-#define GST_PHOTOGRAPHY_IMPL_TEMPLATE(function_name, param_type) \
-static gboolean \
-gst_camerabin_set_ ## function_name (GstPhotography *photo, param_type param) \
-{ \
- GstCameraBin *camera; \
- gboolean ret = FALSE; \
- g_return_val_if_fail (photo != NULL, FALSE); \
- camera = GST_CAMERABIN (photo); \
- if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) { \
- ret = gst_photography_set_ ## function_name (GST_PHOTOGRAPHY (camera->src_vid_src), param); \
- } \
- return ret; \
-} \
-static gboolean \
-gst_camerabin_get_ ## function_name (GstPhotography *photo, param_type * param) \
-{ \
- GstCameraBin *camera; \
- gboolean ret = FALSE; \
- g_return_val_if_fail (photo != NULL, FALSE); \
- camera = GST_CAMERABIN (photo); \
- if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) { \
- ret = gst_photography_get_ ## function_name (GST_PHOTOGRAPHY (camera->src_vid_src), param); \
- } \
- return ret; \
-}
-
-GST_PHOTOGRAPHY_IMPL_TEMPLATE (ev_compensation, gfloat);
-GST_PHOTOGRAPHY_IMPL_TEMPLATE (iso_speed, guint);
-GST_PHOTOGRAPHY_IMPL_TEMPLATE (white_balance_mode, GstWhiteBalanceMode);
-GST_PHOTOGRAPHY_IMPL_TEMPLATE (colour_tone_mode, GstColourToneMode);
-GST_PHOTOGRAPHY_IMPL_TEMPLATE (flash_mode, GstFlashMode);
+ if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+ ret =
+ gst_photography_get_ev_compensation (GST_PHOTOGRAPHY
+ (camera->src_vid_src), ev_compensation);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_set_iso_speed (GstPhotography * photo, guint iso_speed)
+{
+ GstCameraBin *camera;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (photo != NULL, FALSE);
+
+ camera = GST_CAMERABIN (photo);
+
+ /* Cache the setting */
+ camera->photo_settings.iso_speed = iso_speed;
+
+ if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+ ret = gst_photography_set_iso_speed (GST_PHOTOGRAPHY (camera->src_vid_src),
+ iso_speed);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_get_iso_speed (GstPhotography * photo, guint * iso_speed)
+{
+ GstCameraBin *camera;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (photo != NULL, FALSE);
+
+ camera = GST_CAMERABIN (photo);
+
+ if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+ ret = gst_photography_get_iso_speed (GST_PHOTOGRAPHY (camera->src_vid_src),
+ iso_speed);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_set_white_balance_mode (GstPhotography * photo,
+ GstWhiteBalanceMode white_balance_mode)
+{
+ GstCameraBin *camera;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (photo != NULL, FALSE);
+
+ camera = GST_CAMERABIN (photo);
+
+ /* Cache the setting */
+ camera->photo_settings.wb_mode = white_balance_mode;
+
+ if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+ ret =
+ gst_photography_set_white_balance_mode (GST_PHOTOGRAPHY
+ (camera->src_vid_src), white_balance_mode);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_get_white_balance_mode (GstPhotography * photo,
+ GstWhiteBalanceMode * white_balance_mode)
+{
+ GstCameraBin *camera;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (photo != NULL, FALSE);
+
+ camera = GST_CAMERABIN (photo);
+
+ if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+ ret =
+ gst_photography_get_white_balance_mode (GST_PHOTOGRAPHY
+ (camera->src_vid_src), white_balance_mode);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_set_colour_tone_mode (GstPhotography * photo,
+ GstColourToneMode colour_tone_mode)
+{
+ GstCameraBin *camera;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (photo != NULL, FALSE);
+
+ camera = GST_CAMERABIN (photo);
+
+ /* Cache the setting */
+ camera->photo_settings.tone_mode = colour_tone_mode;
+
+ if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+ ret =
+ gst_photography_set_colour_tone_mode (GST_PHOTOGRAPHY
+ (camera->src_vid_src), colour_tone_mode);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_get_colour_tone_mode (GstPhotography * photo,
+ GstColourToneMode * colour_tone_mode)
+{
+ GstCameraBin *camera;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (photo != NULL, FALSE);
+
+ camera = GST_CAMERABIN (photo);
+
+ if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+ ret =
+ gst_photography_get_colour_tone_mode (GST_PHOTOGRAPHY
+ (camera->src_vid_src), colour_tone_mode);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_set_flash_mode (GstPhotography * photo, GstFlashMode flash_mode)
+{
+ GstCameraBin *camera;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (photo != NULL, FALSE);
+
+ camera = GST_CAMERABIN (photo);
+
+ /* Cache the setting */
+ camera->photo_settings.flash_mode = flash_mode;
+
+ if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+ ret = gst_photography_set_flash_mode (GST_PHOTOGRAPHY (camera->src_vid_src),
+ flash_mode);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_get_flash_mode (GstPhotography * photo, GstFlashMode * flash_mode)
+{
+ GstCameraBin *camera;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (photo != NULL, FALSE);
+
+ camera = GST_CAMERABIN (photo);
+
+ if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+ ret = gst_photography_get_flash_mode (GST_PHOTOGRAPHY (camera->src_vid_src),
+ flash_mode);
+ }
+ return ret;
+}
static gboolean
gst_camerabin_set_zoom (GstPhotography * photo, gfloat zoom)
@@ -106,24 +275,10 @@ gst_camerabin_set_scene_mode (GstPhotography * photo, GstSceneMode scene_mode)
camera = GST_CAMERABIN (photo);
- if (scene_mode == GST_PHOTOGRAPHY_SCENE_MODE_NIGHT) {
- GST_DEBUG ("enabling night mode, lowering fps");
- /* Make camerabin select the lowest allowed frame rate */
- camera->night_mode = TRUE;
- /* Remember frame rate before setting night mode */
- camera->pre_night_fps_n = camera->fps_n;
- camera->pre_night_fps_d = camera->fps_d;
- g_signal_emit_by_name (camera, "user-res-fps", camera->width,
- camera->height, 0, 0, 0);
- } else {
- if (camera->night_mode) {
- GST_DEBUG ("disabling night mode, restoring fps to %d/%d",
- camera->pre_night_fps_n, camera->pre_night_fps_d);
- camera->night_mode = FALSE;
- g_signal_emit_by_name (camera, "user-res-fps", camera->width,
- camera->height, camera->pre_night_fps_n, camera->pre_night_fps_d, 0);
- }
- }
+ /* Cache the setting */
+ camera->photo_settings.scene_mode = scene_mode;
+
+ gst_camerabin_handle_scene_mode (camera, scene_mode);
if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
ret = gst_photography_set_scene_mode (GST_PHOTOGRAPHY (camera->src_vid_src),
@@ -186,6 +341,69 @@ gst_camerabin_set_autofocus (GstPhotography * photo, gboolean on)
}
}
+static gboolean
+gst_camerabin_set_config (GstPhotography * photo, GstPhotoSettings * config)
+{
+ GstCameraBin *camera;
+ gboolean ret = FALSE;
+ g_return_val_if_fail (photo != NULL, FALSE);
+ camera = GST_CAMERABIN (photo);
+
+ /* Cache the settings */
+ memcpy (&camera->photo_settings, config, sizeof (GstPhotoSettings));
+
+ /* Handle night mode */
+ gst_camerabin_handle_scene_mode (camera, config->scene_mode);
+
+ if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+ ret =
+ gst_photography_set_config (GST_PHOTOGRAPHY (camera->src_vid_src),
+ config);
+ }
+ return ret;
+}
+
+static gboolean
+gst_camerabin_get_config (GstPhotography * photo, GstPhotoSettings * config)
+{
+ GstCameraBin *camera;
+ gboolean ret = FALSE;
+ g_return_val_if_fail (photo != NULL, FALSE);
+ camera = GST_CAMERABIN (photo);
+ if (PHOTOGRAPHY_IS_OK (camera->src_vid_src)) {
+ ret =
+ gst_photography_get_config (GST_PHOTOGRAPHY (camera->src_vid_src),
+ config);
+ }
+ return ret;
+}
+
+static void
+gst_camerabin_handle_scene_mode (GstCameraBin * camera, GstSceneMode scene_mode)
+{
+ if (scene_mode == GST_PHOTOGRAPHY_SCENE_MODE_NIGHT) {
+ if (!camera->night_mode) {
+ GST_DEBUG ("enabling night mode, lowering fps");
+ /* Make camerabin select the lowest allowed frame rate */
+ camera->night_mode = TRUE;
+ /* Remember frame rate before setting night mode */
+ camera->pre_night_fps_n = camera->fps_n;
+ camera->pre_night_fps_d = camera->fps_d;
+ g_signal_emit_by_name (camera, "user-res-fps", camera->width,
+ camera->height, 0, 0, 0);
+ } else {
+ GST_DEBUG ("night mode already enabled");
+ }
+ } else {
+ if (camera->night_mode) {
+ GST_DEBUG ("disabling night mode, restoring fps to %d/%d",
+ camera->pre_night_fps_n, camera->pre_night_fps_d);
+ camera->night_mode = FALSE;
+ g_signal_emit_by_name (camera, "user-res-fps", camera->width,
+ camera->height, camera->pre_night_fps_n, camera->pre_night_fps_d, 0);
+ }
+ }
+}
void
gst_camerabin_photography_init (GstPhotographyInterface * iface)
@@ -219,4 +437,7 @@ gst_camerabin_photography_init (GstPhotographyInterface * iface)
iface->get_capabilities = gst_camerabin_get_capabilities;
iface->set_autofocus = gst_camerabin_set_autofocus;
+
+ iface->set_config = gst_camerabin_set_config;
+ iface->get_config = gst_camerabin_get_config;
}
diff --git a/gst/camerabin/gstcamerabinxoverlay.c b/gst/camerabin/gstcamerabinxoverlay.c
deleted file mode 100644
index 7a84765a..00000000
--- a/gst/camerabin/gstcamerabinxoverlay.c
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * GStreamer
- * Copyright (C) 2008 Nokia Corporation <multimedia@maemo.org>
- *
- * 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.
- */
-
-/*
- * Includes
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "gstcamerabinxoverlay.h"
-#include "gstcamerabin.h"
-
-/*
- * static functions implementation
- */
-
-static void
-gst_camerabin_expose (GstXOverlay * overlay)
-{
- if (overlay && GST_CAMERABIN (overlay)->view_sink) {
- GstXOverlay *xoverlay = GST_X_OVERLAY (GST_CAMERABIN (overlay)->view_sink);
- gst_x_overlay_expose (xoverlay);
- }
-}
-
-static void
-gst_camerabin_set_xwindow_id (GstXOverlay * overlay, gulong xwindow_id)
-{
- if (overlay && GST_CAMERABIN (overlay)->view_sink) {
- GstXOverlay *xoverlay = GST_X_OVERLAY (GST_CAMERABIN (overlay)->view_sink);
- gst_x_overlay_set_xwindow_id (xoverlay, xwindow_id);
- }
-}
-
-static void
-gst_camerabin_set_event_handling (GstXOverlay * overlay, gboolean handle_events)
-{
- if (overlay && GST_CAMERABIN (overlay)->view_sink) {
- GstXOverlay *xoverlay = GST_X_OVERLAY (GST_CAMERABIN (overlay)->view_sink);
- gst_x_overlay_handle_events (xoverlay, handle_events);
- }
-}
-
-/*
- * extern functions implementation
- */
-
-void
-gst_camerabin_xoverlay_init (GstXOverlayClass * iface)
-{
- iface->set_xwindow_id = gst_camerabin_set_xwindow_id;
- iface->expose = gst_camerabin_expose;
- iface->handle_events = gst_camerabin_set_event_handling;
-}
diff --git a/gst/camerabin/gstcamerabinxoverlay.h b/gst/camerabin/gstcamerabinxoverlay.h
deleted file mode 100644
index b9e9d9b6..00000000
--- a/gst/camerabin/gstcamerabinxoverlay.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * GStreamer
- * Copyright (C) 2008 Nokia Corporation <multimedia@maemo.org>
- *
- * 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_CAMERAXOVERLAY_H__
-#define __GST_CAMERAXOVERLAY_H__
-
-#include <gst/interfaces/xoverlay.h>
-
-extern void gst_camerabin_xoverlay_init (GstXOverlayClass * iface);
-
-#endif /* #ifndef __GST_CAMERAXOVERLAY_H__ */
diff --git a/gst/debugutils/Makefile.am b/gst/debugutils/Makefile.am
index d1e1430e..3b93fa91 100644
--- a/gst/debugutils/Makefile.am
+++ b/gst/debugutils/Makefile.am
@@ -2,7 +2,7 @@ plugin_LTLIBRARIES = libgstdebugutilsbad.la
libgstdebugutilsbad_la_SOURCES = fpsdisplaysink.c debugutilsbad.c
libgstdebugutilsbad_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS)
-libgstdebugutilsbad_la_LIBADD = $(GST_BASE_LIBS) -lgstinterfaces-$(GST_MAJORMINOR)
+libgstdebugutilsbad_la_LIBADD = $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) -lgstinterfaces-$(GST_MAJORMINOR)
libgstdebugutilsbad_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstdebugutilsbad_la_LIBTOOLFLAGS = --tag=disable-static
diff --git a/gst/dvdspu/gstdvdspu.c b/gst/dvdspu/gstdvdspu.c
index f5fce4bc..af8bc99e 100644
--- a/gst/dvdspu/gstdvdspu.c
+++ b/gst/dvdspu/gstdvdspu.c
@@ -716,12 +716,11 @@ gst_dvd_spu_redraw_still (GstDVDSpu * dvdspu, gboolean force)
static void
gst_dvd_spu_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event)
{
- const GstStructure *structure = gst_event_get_structure (event);
- const gchar *event_type = gst_structure_get_string (structure, "event");
gboolean hl_change = FALSE;
GST_INFO_OBJECT (dvdspu, "DVD event of type %s on subp pad OOB=%d",
- event_type, (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB));
+ gst_structure_get_string (event->structure, "event"),
+ (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB));
switch (dvdspu->spu_input_type) {
case SPU_INPUT_TYPE_VOBSUB:
diff --git a/gst/frei0r/Makefile.am b/gst/frei0r/Makefile.am
new file mode 100644
index 00000000..400801d9
--- /dev/null
+++ b/gst/frei0r/Makefile.am
@@ -0,0 +1,15 @@
+plugin_LTLIBRARIES = libgstfrei0r.la
+
+libgstfrei0r_la_SOURCES = \
+ gstfrei0r.c \
+ gstfrei0rfilter.c \
+ gstfrei0rsrc.c \
+ gstfrei0rmixer.c
+
+libgstfrei0r_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS)
+libgstfrei0r_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) \
+ -lgstvideo-@GST_MAJORMINOR@
+libgstfrei0r_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstfrei0r_la_LIBTOOLFLAGS = --tag=disable-static
+
+noinst_HEADERS = gstfrei0r.h gstfrei0rfilter.h gstfrei0rsrc.h gstfrei0rmixer.h frei0r.h
diff --git a/gst/frei0r/frei0r.h b/gst/frei0r/frei0r.h
new file mode 100644
index 00000000..f6b81d2b
--- /dev/null
+++ b/gst/frei0r/frei0r.h
@@ -0,0 +1,567 @@
+/** @mainpage frei0r - a minimalistic plugin API for video effects
+ *
+ * @section sec_intro Introduction
+ *
+ * This is frei0r - a minimalistic plugin API for video effects.
+ *
+ * The main emphasis is on simplicity - there are many different applications
+ * that use video effects, and they all have different requirements regarding
+ * their internal plugin API. And that's why frei0r does not try to be a
+ * one-in-all general video plugin API, but instead an API for the most
+ * common video effects: simple filters, sources and mixers that can be
+ * controlled by parameters.
+ *
+ * It's our hope that this way these simple effects can be shared between
+ * many applications, avoiding their reimplementation by different
+ * projects.
+ *
+ * On the other hand, this is not meant as a competing standard to
+ * more ambitious efforts that try to satisfy the needs of many different
+ * applications and more complex effects.
+ *
+ *
+ * @section sec_overview Overview
+ *
+ * If you are new to frei0r, the best thing is probably to have
+ * a look at the <a href="frei0r_8h-source.html">frei0r header</a>,
+ * which is quite simple.
+ *
+ * After that, you might want to look at the
+ * <a href="frei0r_8h.html">frei0r functions</a> in more detail.
+ *
+ * When developing a new frei0r effect, you have to choose
+ * - which effect type to use (\ref PLUGIN_TYPE),
+ * - which color model to use (\ref COLOR_MODEL), and
+ * - which parameter types (\ref PARAM_TYPE) your effect will support.
+ *
+ * To round things up, you should decide whether your effect should have
+ * an associated icon (\ref icons), and where it will be installed
+ * (\ref pluglocations).
+ *
+ * @section sec_changes Changes
+ *
+ * @subsection sec_changes_1_0_1_1 From frei0r 1.0 to frei0r 1.1
+ *
+ * - added specifications for plugin locations
+ * - added specifications for frei0r icons
+ * - added RGBA8888 color model
+ * - added packed32 color model
+ * - added better specification of color models
+ * - added string type
+ * - added bounds to resolution (8 <= width, height <= 2048)
+ * - width and height must be an integer multiple of 8
+ * - frame data must be 16 byte aligned
+ * - improved update specification (must not change parameters,
+ * must restore fpu state)
+ * - added note for applications to ignore effects with unknown fields
+ * - added new plugin types mixer2 and mixer3
+ * - added section about \ref concurrency
+ */
+
+
+/**
+ * \addtogroup pluglocations Plugin Locations
+ * @section sec_pluglocations Plugin Locations
+ *
+ * For Unix platforms there are rules for the location of frei0r plugins.
+ *
+ * frei0r 1.x plugin files should be located in
+ *
+ * - (1) /usr/lib/frei0r-1/\<vendor\>
+ * - (2) /usr/local/lib/frei0r-1/\<vendor\>
+ * - (3) $HOME/.frei0r-1/lib/\<vendor\>
+ *
+ * Examples:
+ *
+ * - /usr/lib/frei0r-1/mob/flippo.so
+ * - /usr/lib/frei0r-1/drone/flippo.so
+ * - /usr/local/lib/frei0r-1/gephex/coma/invert0r.so
+ * - /home/martin/.frei0r-1/lib/martin/test.so
+ *
+ * Like in these examples plugins should be placed in "vendor" subdirs
+ * to reduce name clashes.
+ *
+ * @subsection sec_order Plugin Loading Order
+ *
+ * The application shall load plugins in the following order: 3, 2, 1.
+ * If a name clash occurs (two or more frei0r plugins with identical
+ * effect name), the plugins in directory 3 have precedence over plugins
+ * in directory 2, and those in directory 2 have precedence over plugins
+ * in directory 1.
+ *
+ * This makes it possible for users to "override" effects that are
+ * installed in system wide directories by placing plugins in their
+ * home directory.
+ *
+ * The order of loading plugins inside each of the directories
+ * 1, 2, and 3 is not defined.
+ */
+
+/**
+ *\addtogroup icons Icons for frei0r effects
+ * @section sec_icons Icons for frei0r effects
+ *
+ * Each frei0r effect can have an associated icon.
+ *
+ * @subsection sec_icon_format Icon Format
+ *
+ * The format of frei0r icons must be png.
+ * Recommended resolution is 64x64.
+ * The icon filename of an effect with effect name "frei0r"
+ * must be "frei0r.png".
+ *
+ * @subsection sec_icon_location Icon location
+ *
+ * The exact location where the application should look for the
+ * plugin is platform dependant.
+ *
+ * For Windows platforms, the icon should be at the same place as
+ * the plugin containing the effect.
+ *
+ * For Unix platforms, the following mapping from plugin location
+ * to icon location must be used:
+ *
+ * Let \<plugin_path\>/\<plugin\> be a frei0r plugin with name \<effect_name\>.
+ * Then the corresponding icon (if any) shall be located in
+ * \<icon_path\>/\<effect_name\>.png.
+ * \<icon_path\> can be obtained in the following way:
+ *
+ * @verbatim
+ <plugin_path> | <icon_path>
+ ----------------------------------------------------------------------------
+ $HOME/.frei0r-1/lib/<vendor> | $HOME/.frei0r-1/icons/<vendor>
+ /usr/local/lib/frei0r-1/<vendor> | /usr/local/share/frei0r-1/icons/<vendor>
+ /usr/lib/frei0r-1/<vendor> | /usr/share/frei0r-1/icons/<vendor>
+ * | <plugin_path>
+ @endverbatim
+ *
+ * (The wildcard '*' stands for any other plugin_path)
+ *
+ * For other platforms, no location is defined. We recommend to use the
+ * plugin path where possible.
+ */
+
+/**
+ * \addtogroup concurrency Concurrency
+ * @section sec_concurrency Concurrency
+ *
+ * - \ref f0r_init
+ * - \ref f0r_deinit
+ *
+ * These methods must not be called more than once. It is obvious that no
+ * concurrent calls are allowed.
+ *
+ *
+ * - \ref f0r_get_plugin_info
+ * - \ref f0r_get_param_info
+ * - \ref f0r_construct
+ * - \ref f0r_destruct
+ *
+ * Concurrent calls of these functions are allowed.
+ *
+ *
+ * - \ref f0r_set_param_value
+ * - \ref f0r_get_param_value
+ * - \ref f0r_update
+ * - \ref f0r_update2
+ *
+ * If a thread is in one of these methods its allowed for another thread to
+ * enter one of theses methods for a different effect instance. But for one
+ * effect instance only one thread is allowed to execute any of these methods.
+ */
+
+
+
+/** \file
+ * \brief This file defines the frei0r api, version 1.1.
+ *
+ * A conforming plugin must implement and export all functions declared in
+ * this header.
+ *
+ * A conforming application must accept only those plugins which use
+ * allowed values for the described fields.
+ */
+
+#ifndef INCLUDED_FREI0R_H
+#define INCLUDED_FREI0R_H
+
+#include <inttypes.h>
+
+/**
+ * The frei0r API major version
+ */
+#define FREI0R_MAJOR_VERSION 1
+
+/**
+ * The frei0r API minor version
+ */
+#define FREI0R_MINOR_VERSION 1
+
+//---------------------------------------------------------------------------
+
+/**
+ * f0r_init() is called once when the plugin is loaded by the application.
+ * \see f0r_deinit
+ */
+int f0r_init();
+
+/**
+ * f0r_deinit is called once when the plugin is unloaded by the application.
+ * \see f0r_init
+ */
+void f0r_deinit();
+
+//---------------------------------------------------------------------------
+
+/** \addtogroup PLUGIN_TYPE Type of the Plugin
+ * These defines determine whether the plugin is a
+ * source, a filter or one of the two mixer types
+ * @{
+ */
+
+/** one input and one output */
+#define F0R_PLUGIN_TYPE_FILTER 0
+/** just one output */
+#define F0R_PLUGIN_TYPE_SOURCE 1
+/** two inputs and one output */
+#define F0R_PLUGIN_TYPE_MIXER2 2
+/** three inputs and one output */
+#define F0R_PLUGIN_TYPE_MIXER3 3
+
+/** @} */
+
+//---------------------------------------------------------------------------
+
+/** \addtogroup COLOR_MODEL Color Models
+ * List of supported color models.
+ *
+ * Note: the color models are endian independent, because the
+ * color components are defined by their positon in memory, not
+ * by their significance in an uint32_t value.
+ *
+ * For effects that work on the color components,
+ * RGBA8888 is the recommended color model for frei0r-1.1 effects.
+ * For effects that only work on pixels, PACKED32 is the recommended
+ * color model since it helps the application to avoid unnecessary
+ * color conversions.
+ *
+ * Effects can choose an appropriate color model, applications must support
+ * all color models and do conversions if necessary. Source effects
+ * must not use the PACKED32 color model because the application must know
+ * in which color model the created framebuffers are represented.
+ *
+ * For each color model, a frame consists of width*height pixels which
+ * are stored row-wise and consecutively in memory. The size of a pixel is
+ * 4 bytes. There is no extra pitch parameter
+ * (i.e. the pitch is simply width*4).
+ *
+ * The following additional constraints must be honored:
+ * - The top-most line of a frame is stored first in memory.
+ * - A frame must be aligned to a 16 byte border in memory.
+ * - The width and height of a frame must be positive
+ * - The width and height of a frame must be integer multiples of 8
+ *
+ * These constraints make sure that each line is stored at an address aligned
+ * to 16 byte.
+ */
+/*@{*/
+/**
+ * In BGRA8888, each pixel is represented by 4 consecutive
+ * unsigned bytes, where the first byte value represents
+ * the blue, the second the green, and the third the red color
+ * component of the pixel. The last value represents the
+ * alpha value.
+ */
+#define F0R_COLOR_MODEL_BGRA8888 0
+
+/**
+ * In RGBA8888, each pixel is represented by 4 consecutive
+ * unsigned bytes, where the first byte value represents
+ * the red, the second the green, and the third the blue color
+ * component of the pixel. The last value represents the
+ * alpha value.
+ */
+#define F0R_COLOR_MODEL_RGBA8888 1
+
+/**
+ * In PACKED32, each pixel is represented by 4 consecutive
+ * bytes, but it is not defined how the color componets are
+ * stored. The true color format could be RGBA8888,
+ * BGRA8888, a packed 32 bit YUV format, or any other
+ * color format that stores pixels in 32 bit.
+ *
+ * This is useful for effects that don't work on color but
+ * only on pixels (for example a mirror effect).
+ *
+ * Note that source effects must not use this color model.
+ */
+#define F0R_COLOR_MODEL_PACKED32 2
+/*@}*/
+
+/**
+ * The f0r_plugin_info_t structure is filled in by the plugin
+ * to tell the application about its name, type, number of parameters,
+ * and version.
+ *
+ * An application should ignore (i.e. not use) frei0r effects that
+ * have unknown values in the plugin_type or color_model field.
+ * It should also ignore effects with a too high frei0r_version.
+ *
+ * This is necessary to be able to extend the frei0r spec (e.g.
+ * by adding new color models or plugin types) in a way that does not
+ * result in crashes when loading effects that make use of these
+ * extensions into an older application.
+ *
+ * All strings are unicode, 0-terminated, and the encoding is utf-8.
+ */
+typedef struct f0r_plugin_info
+{
+ const char* name; /**< The (short) name of the plugin */
+ const char* author; /**< The plugin author */
+ /** The plugin type
+ * \see PLUGIN_TYPE
+ */
+ int plugin_type;
+ int color_model; /**< The color model used */
+ int frei0r_version; /**< The frei0r major version this plugin is built for*/
+ int major_version; /**< The major version of the plugin */
+ int minor_version; /**< The minor version of the plugin */
+ int num_params; /**< The number of parameters of the plugin */
+ const char* explanation; /**< An optional explanation string */
+} f0r_plugin_info_t;
+
+
+/**
+ * Is called once after init. The plugin has to fill in the values in info.
+ *
+ * \param info Pointer to an info struct allocated by the application.
+ */
+void f0r_get_plugin_info(f0r_plugin_info_t* info);
+
+//---------------------------------------------------------------------------
+
+/** \addtogroup PARAM_TYPE Parameter Types
+ *
+ * @{
+ */
+
+
+/**
+ * Parameter type for boolean values
+ * \see f0r_param_bool
+ */
+#define F0R_PARAM_BOOL 0
+
+/**
+ * Parameter type for doubles
+ * \see f0r_param_double
+ */
+#define F0R_PARAM_DOUBLE 1
+
+/**
+ * Parameter type for color
+ * \see f0r_param_color
+ */
+#define F0R_PARAM_COLOR 2
+/**
+ * Parameter type for position
+ * \see f0r_param_position
+ */
+#define F0R_PARAM_POSITION 3
+
+/**
+ * Parameter type for string
+ * \see f0r_param_string
+ */
+#define F0R_PARAM_STRING 4
+
+/**
+ * The boolean type. The allowed range of values is [0, 1].
+ * [0, 0.5[ is mapped to false and [0.5, 1] is mapped to true.
+ */
+typedef double f0r_param_bool;
+
+/**
+ * The double type. The allowed range of values is [0, 1].
+ */
+typedef double f0r_param_double;
+
+/**
+ * The color type. All three color components are in the range [0, 1].
+ */
+typedef struct f0r_param_color
+{
+ float r; /**< red color component */
+ float g; /**< green color component */
+ float b; /**< blue color component */
+} f0r_param_color_t;
+
+/**
+ * The position type. Both position coordinates are in the range [0, 1].
+ */
+typedef struct f0r_param_position
+{
+ double x; /**< x coordinate */
+ double y; /**< y coordinate */
+} f0r_param_position_t;
+
+
+/**
+ * The string type.
+ * Zero terminated array of 8-bit values in utf-8 encoding
+ */
+typedef char f0r_param_string;
+
+/** @} */
+
+
+/**
+ * Similar to f0r_plugin_info_t, this structure is filled by the plugin
+ * for every parameter.
+ *
+ * All strings are unicode, 0-terminated, and the encoding is utf-8.
+ */
+typedef struct f0r_param_info
+{
+ const char* name; /**<The (short) name of the param */
+ int type; /**<The type (see the F0R_PARAM_* defines) */
+ const char* explanation; /**<Optional explanation (can be 0) */
+} f0r_param_info_t;
+
+/**
+ * f0r_get_param_info is called by the application to query the type of
+ * each parameter.
+ *
+ * \param info is allocated by the application and filled by the plugin
+ * \param param_index the index of the parameter to be queried (from 0 to
+ * num_params-1)
+ */
+void f0r_get_param_info(f0r_param_info_t* info, int param_index);
+
+//---------------------------------------------------------------------------
+
+/**
+ * Transparent instance pointer of the frei0r effect.
+ */
+typedef void* f0r_instance_t;
+
+/**
+ * Constructor for effect instances. The plugin returns a pointer to
+ * its internal instance structure.
+ *
+ * The resolution has to be an integer multiple of 8,
+ * must be greater than 0 and be at most 2048 in both dimensions.
+ *
+ * \param width The x-resolution of the processed video frames
+ * \param height The y-resolution of the processed video frames
+ * \returns 0 on failure or a pointer != 0 on success
+ *
+ * \see f0r_destruct
+ */
+f0r_instance_t f0r_construct(unsigned int width, unsigned int height);
+
+/**
+ * Destroys an effect instance.
+ *
+ * \param instance The pointer to the plugins internal instance structure.
+ *
+ * \see f0r_construct
+ */
+void f0r_destruct(f0r_instance_t instance);
+
+//---------------------------------------------------------------------------
+
+/**
+ * Transparent parameter handle.
+ */
+typedef void* f0r_param_t;
+
+/**
+ * This function allows the application to set the parameter values of an
+ * effect instance. Validity of the parameter pointer is handled by the
+ * application thus the data must be copied by the effect.
+ *
+ * \param instance the effect instance
+ * \param param pointer to the parameter value
+ * \param param_index index of the parameter
+ *
+ * \see f0r_get_param_value
+ */
+void f0r_set_param_value(f0r_instance_t instance,
+ f0r_param_t param, int param_index);
+
+/**
+ * This function allows the application to query the parameter values of an
+ * effect instance.
+ *
+ * \param instance the effect instance
+ * \param param pointer to the parameter value
+ * \param param_index index of the parameter
+ *
+ * \see f0r_set_param_value
+ */
+void f0r_get_param_value(f0r_instance_t instance,
+ f0r_param_t param, int param_index);
+
+//---------------------------------------------------------------------------
+
+/**
+ * This is where the core effect processing happens. The application calls it
+ * after it has set the necessary parameter values.
+ * inframe and outframe must be aligned to an integer multiple of 16 bytes
+ * in memory.
+ *
+ * This funcition should not alter the parameters of the effect in any
+ * way (\ref f0r_get_param_value should return the same values after a call
+ * to \ref f0r_update as before the call).
+ *
+ * The function is responsible to restore the fpu state (e.g. rounding mode)
+ * and mmx state if applicable before it returns to the caller.
+ *
+ * The host mustn't call \ref f0r_update for effects of type
+ * \ref F0R_PLUGIN_TYPE_MIXER2 and \ref F0R_PLUGIN_TYPE_MIXER3.
+ *
+ * \param instance the effect instance
+ * \param time the application time in seconds but with subsecond resolution
+ * (e.g. milli-second resolution). The resolution should be at least
+ * the inter-frame period of the application.
+ * \param inframe the incoming video frame (can be zero for sources)
+ * \param outframe the resulting video frame
+ *
+ * \see f0r_update2
+ */
+void f0r_update(f0r_instance_t instance,
+ double time, const uint32_t* inframe, uint32_t* outframe);
+
+//---------------------------------------------------------------------------
+
+/**
+ * For effects of type \ref F0R_PLUGIN_TYPE_SOURCE or
+ * \ref F0R_PLUGIN_TYPE_FILTER this method is optional. The \ref f0r_update
+ * method must still be exported for these two effect types. If both are
+ * provided the behavior of them must be the same.
+ *
+ * Effects of type \ref F0R_PLUGIN_TYPE_MIXER2 or \ref F0R_PLUGIN_TYPE_MIXER3 must provide the new \ref f0r_update2 method.
+
+ * \param instance the effect instance
+ * \param time the application time in seconds but with subsecond resolution
+ * (e.g. milli-second resolution). The resolution should be at least
+ * the inter-frame period of the application.
+ * \param inframe1 the first incoming video frame (can be zero for sources)
+ * \param inframe2 the second incoming video frame
+ (can be zero for sources and filters)
+ * \param inframe3 the third incoming video frame
+ (can be zero for sources, filters and mixer3)
+ * \param outframe the resulting video frame
+ *
+ * \see f0r_update
+ */
+void f0r_update2(f0r_instance_t instance,
+ double time,
+ const uint32_t* inframe1,
+ const uint32_t* inframe2,
+ const uint32_t* inframe3,
+ uint32_t* outframe);
+//---------------------------------------------------------------------------
+
+#endif
diff --git a/gst/frei0r/gstfrei0r.c b/gst/frei0r/gstfrei0r.c
new file mode 100644
index 00000000..0f7ba5c5
--- /dev/null
+++ b/gst/frei0r/gstfrei0r.c
@@ -0,0 +1,598 @@
+/* GStreamer
+ * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * 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 "gstfrei0r.h"
+#include "gstfrei0rfilter.h"
+#include "gstfrei0rsrc.h"
+#include "gstfrei0rmixer.h"
+
+#include <string.h>
+
+GST_DEBUG_CATEGORY (frei0r_debug);
+#define GST_CAT_DEFAULT frei0r_debug
+
+GstCaps *
+gst_frei0r_caps_from_color_model (gint color_model)
+{
+ switch (color_model) {
+ case F0R_COLOR_MODEL_BGRA8888:
+ return gst_caps_from_string (GST_VIDEO_CAPS_BGRA);
+ case F0R_COLOR_MODEL_RGBA8888:
+ return gst_caps_from_string (GST_VIDEO_CAPS_RGBA);
+ case F0R_COLOR_MODEL_PACKED32:
+ return gst_caps_from_string (GST_VIDEO_CAPS_BGRA " ; "
+ GST_VIDEO_CAPS_RGBA " ; "
+ GST_VIDEO_CAPS_ABGR " ; "
+ GST_VIDEO_CAPS_ARGB " ; "
+ GST_VIDEO_CAPS_BGRx " ; "
+ GST_VIDEO_CAPS_RGBx " ; "
+ GST_VIDEO_CAPS_xBGR " ; "
+ GST_VIDEO_CAPS_xRGB " ; " GST_VIDEO_CAPS_YUV ("AYUV"));
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+void
+gst_frei0r_klass_install_properties (GObjectClass * gobject_class,
+ GstFrei0rFuncTable * ftable, GstFrei0rProperty * properties,
+ gint n_properties)
+{
+ gint i, count = 1;
+ f0r_instance_t *instance = ftable->construct (640, 480);
+
+ g_assert (instance);
+
+ for (i = 0; i < n_properties; i++) {
+ f0r_param_info_t *param_info = &properties[i].info;
+ gchar *prop_name;
+
+ ftable->get_param_info (param_info, i);
+
+ prop_name = g_ascii_strdown (param_info->name, -1);
+ g_strcanon (prop_name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-+", '-');
+
+ properties[i].prop_id = count;
+ properties[i].prop_idx = i;
+
+ ftable->get_param_value (instance, &properties[i].default_value, i);
+ if (param_info->type == F0R_PARAM_STRING)
+ properties[i].default_value.data.s =
+ g_strdup (properties[i].default_value.data.s);
+
+ switch (param_info->type) {
+ case F0R_PARAM_BOOL:
+ g_object_class_install_property (gobject_class, count++,
+ g_param_spec_boolean (prop_name, param_info->name,
+ param_info->explanation, FALSE,
+ G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+ properties[i].n_prop_ids = 1;
+ break;
+ case F0R_PARAM_DOUBLE:
+ g_object_class_install_property (gobject_class, count++,
+ g_param_spec_double (prop_name, param_info->name,
+ param_info->explanation, -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+ properties[i].n_prop_ids = 1;
+ break;
+ case F0R_PARAM_STRING:
+ g_object_class_install_property (gobject_class, count++,
+ g_param_spec_string (prop_name, param_info->name,
+ param_info->explanation, NULL,
+ G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+ properties[i].n_prop_ids = 1;
+ break;
+ case F0R_PARAM_COLOR:{
+ gchar *prop_name_full;
+ gchar *prop_nick_full;
+
+ prop_name_full = g_strconcat (prop_name, "-r", NULL);
+ prop_nick_full = g_strconcat (param_info->name, "-R", NULL);
+ g_object_class_install_property (gobject_class, count++,
+ g_param_spec_float (prop_name_full, prop_nick_full,
+ param_info->explanation, 0.0, 1.0, 0.0,
+ G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+ g_free (prop_name_full);
+ g_free (prop_nick_full);
+
+ prop_name_full = g_strconcat (prop_name, "-g", NULL);
+ prop_nick_full = g_strconcat (param_info->name, "-G", NULL);
+ g_object_class_install_property (gobject_class, count++,
+ g_param_spec_float (prop_name_full, param_info->name,
+ param_info->explanation, 0.0, 1.0, 0.0,
+ G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+ g_free (prop_name_full);
+ g_free (prop_nick_full);
+
+ prop_name_full = g_strconcat (prop_name, "-b", NULL);
+ prop_nick_full = g_strconcat (param_info->name, "-B", NULL);
+ g_object_class_install_property (gobject_class, count++,
+ g_param_spec_float (prop_name_full, param_info->name,
+ param_info->explanation, 0.0, 1.0, 0.0,
+ G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+ g_free (prop_name_full);
+ g_free (prop_nick_full);
+
+ properties[i].n_prop_ids = 3;
+ break;
+ }
+ case F0R_PARAM_POSITION:{
+ gchar *prop_name_full;
+ gchar *prop_nick_full;
+
+ prop_name_full = g_strconcat (prop_name, "-x", NULL);
+ prop_nick_full = g_strconcat (param_info->name, "-X", NULL);
+ g_object_class_install_property (gobject_class, count++,
+ g_param_spec_double (prop_name_full, param_info->name,
+ param_info->explanation, 0.0, 1.0, 0.0,
+ G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+ g_free (prop_name_full);
+ g_free (prop_nick_full);
+
+ prop_name_full = g_strconcat (prop_name, "-Y", NULL);
+ prop_nick_full = g_strconcat (param_info->name, "-X", NULL);
+ g_object_class_install_property (gobject_class, count++,
+ g_param_spec_double (prop_name_full, param_info->name,
+ param_info->explanation, 0.0, 1.0, 0.0,
+ G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+ g_free (prop_name_full);
+ g_free (prop_nick_full);
+
+ properties[i].n_prop_ids = 2;
+ break;
+ }
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ g_free (prop_name);
+ }
+
+ ftable->destruct (instance);
+}
+
+GstFrei0rPropertyValue *
+gst_frei0r_property_cache_init (GstFrei0rProperty * properties,
+ gint n_properties)
+{
+ gint i;
+ GstFrei0rPropertyValue *ret = g_new0 (GstFrei0rPropertyValue, n_properties);
+
+ for (i = 0; i < n_properties; i++) {
+ memcpy (&ret[i].data, &properties[i].default_value,
+ sizeof (GstFrei0rPropertyValue));
+
+ if (properties[i].info.type == F0R_PARAM_STRING)
+ ret[i].data.s = g_strdup (ret[i].data.s);
+ }
+
+ return ret;
+}
+
+void
+gst_frei0r_property_cache_free (GstFrei0rProperty * properties,
+ GstFrei0rPropertyValue * property_cache, gint n_properties)
+{
+ gint i;
+
+ for (i = 0; i < n_properties; i++) {
+ if (properties[i].info.type == F0R_PARAM_STRING)
+ g_free (property_cache[i].data.s);
+ }
+ g_free (property_cache);
+}
+
+f0r_instance_t *
+gst_frei0r_instance_construct (GstFrei0rFuncTable * ftable,
+ GstFrei0rProperty * properties, gint n_properties,
+ GstFrei0rPropertyValue * property_cache, gint width, gint height)
+{
+ f0r_instance_t *instance = ftable->construct (width, height);
+ gint i;
+
+ for (i = 0; i < n_properties; i++) {
+ if (properties[i].info.type == F0R_PARAM_STRING)
+ ftable->set_param_value (instance, property_cache[i].data.s, i);
+ else
+ ftable->set_param_value (instance, &property_cache[i].data, i);
+ }
+
+ return instance;
+}
+
+gboolean
+gst_frei0r_get_property (f0r_instance_t * instance, GstFrei0rFuncTable * ftable,
+ GstFrei0rProperty * properties, gint n_properties,
+ GstFrei0rPropertyValue * property_cache, guint prop_id, GValue * value)
+{
+ gint i;
+ GstFrei0rProperty *prop = NULL;
+
+ for (i = 0; i < n_properties; i++) {
+ if (properties[i].prop_id <= prop_id &&
+ properties[i].prop_id + properties[i].n_prop_ids > prop_id) {
+ prop = &properties[i];
+ break;
+ }
+ }
+
+ if (!prop)
+ return FALSE;
+
+ switch (prop->info.type) {
+ case F0R_PARAM_BOOL:{
+ gdouble d;
+
+ if (instance)
+ ftable->get_param_value (instance, &d, prop->prop_idx);
+ else
+ d = property_cache[prop->prop_idx].data.b ? 1.0 : 0.0;
+
+ g_value_set_boolean (value, (d < 0.5) ? FALSE : TRUE);
+ break;
+ }
+ case F0R_PARAM_DOUBLE:{
+ gdouble d;
+
+ if (instance)
+ ftable->get_param_value (instance, &d, prop->prop_idx);
+ else
+ d = property_cache[prop->prop_idx].data.d;
+
+ g_value_set_double (value, d);
+ break;
+ }
+ case F0R_PARAM_STRING:{
+ const gchar *s;
+
+ if (instance)
+ ftable->get_param_value (instance, &s, prop->prop_idx);
+ else
+ s = property_cache[prop->prop_idx].data.s;
+ g_value_set_string (value, s);
+ break;
+ }
+ case F0R_PARAM_COLOR:{
+ f0r_param_color_t color;
+
+ if (instance)
+ ftable->get_param_value (instance, &color, prop->prop_idx);
+ else
+ color = property_cache[prop->prop_idx].data.color;
+
+ switch (prop_id - prop->prop_id) {
+ case 0:
+ g_value_set_float (value, color.r);
+ break;
+ case 1:
+ g_value_set_float (value, color.g);
+ break;
+ case 2:
+ g_value_set_float (value, color.b);
+ break;
+ }
+ break;
+ }
+ case F0R_PARAM_POSITION:{
+ f0r_param_position_t position;
+
+ if (instance)
+ ftable->get_param_value (instance, &position, prop->prop_idx);
+ else
+ position = property_cache[prop->prop_idx].data.position;
+
+ switch (prop_id - prop->prop_id) {
+ case 0:
+ g_value_set_double (value, position.x);
+ break;
+ case 1:
+ g_value_set_double (value, position.y);
+ break;
+ }
+ break;
+ }
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gst_frei0r_set_property (f0r_instance_t * instance, GstFrei0rFuncTable * ftable,
+ GstFrei0rProperty * properties, gint n_properties,
+ GstFrei0rPropertyValue * property_cache, guint prop_id,
+ const GValue * value)
+{
+ GstFrei0rProperty *prop = NULL;
+ gint i;
+
+ for (i = 0; i < n_properties; i++) {
+ if (properties[i].prop_id <= prop_id &&
+ properties[i].prop_id + properties[i].n_prop_ids > prop_id) {
+ prop = &properties[i];
+ break;
+ }
+ }
+
+ if (!prop)
+ return FALSE;
+
+ switch (prop->info.type) {
+ case F0R_PARAM_BOOL:{
+ gboolean b = g_value_get_boolean (value);
+ gdouble d = b ? 1.0 : 0.0;
+
+ if (instance)
+ ftable->set_param_value (instance, &d, prop->prop_idx);
+ property_cache[prop->prop_idx].data.b = b;
+ break;
+ }
+ case F0R_PARAM_DOUBLE:{
+ gdouble d = g_value_get_double (value);
+
+ if (instance)
+ ftable->set_param_value (instance, &d, prop->prop_idx);
+ property_cache[prop->prop_idx].data.d = d;
+ break;
+ }
+ case F0R_PARAM_STRING:{
+ gchar *s = g_value_dup_string (value);
+
+ /* Copies the string */
+ if (instance)
+ ftable->set_param_value (instance, s, prop->prop_idx);
+ property_cache[prop->prop_idx].data.s = s;
+ break;
+ }
+ case F0R_PARAM_COLOR:{
+ gfloat f = g_value_get_float (value);
+ f0r_param_color_t *color = &property_cache[prop->prop_idx].data.color;
+
+ switch (prop_id - prop->prop_id) {
+ case 0:
+ color->r = f;
+ break;
+ case 1:
+ color->g = f;
+ break;
+ case 2:
+ color->b = f;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (instance)
+ ftable->set_param_value (instance, color, prop->prop_idx);
+ break;
+ }
+ case F0R_PARAM_POSITION:{
+ gdouble d = g_value_get_double (value);
+ f0r_param_position_t *position =
+ &property_cache[prop->prop_idx].data.position;
+
+ switch (prop_id - prop->prop_id) {
+ case 0:
+ position->x = d;
+ break;
+ case 1:
+ position->y = d;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ if (instance)
+ ftable->set_param_value (instance, position, prop->prop_idx);
+ break;
+ }
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+register_plugin (GstPlugin * plugin, const gchar * filename)
+{
+ GModule *module;
+ gboolean ret = FALSE;
+ GstFrei0rFuncTable ftable = { NULL, };
+ gint i;
+ f0r_plugin_info_t info = { NULL, };
+ f0r_instance_t *instance = NULL;
+
+ GST_DEBUG ("Registering plugin '%s'", filename);
+
+ module = g_module_open (filename, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
+ if (!module) {
+ GST_WARNING ("Failed to load plugin");
+ return FALSE;
+ }
+
+ if (!g_module_symbol (module, "f0r_init", (gpointer *) & ftable.init)) {
+ GST_INFO ("No frei0r plugin");
+ g_module_close (module);
+ return FALSE;
+ }
+
+ if (!g_module_symbol (module, "f0r_deinit", (gpointer *) & ftable.deinit) ||
+ !g_module_symbol (module, "f0r_construct",
+ (gpointer *) & ftable.construct)
+ || !g_module_symbol (module, "f0r_destruct",
+ (gpointer *) & ftable.destruct)
+ || !g_module_symbol (module, "f0r_get_plugin_info",
+ (gpointer *) & ftable.get_plugin_info)
+ || !g_module_symbol (module, "f0r_get_param_info",
+ (gpointer *) & ftable.get_param_info)
+ || !g_module_symbol (module, "f0r_set_param_value",
+ (gpointer *) & ftable.set_param_value)
+ || !g_module_symbol (module, "f0r_get_param_value",
+ (gpointer *) & ftable.get_param_value))
+ goto invalid_frei0r_plugin;
+
+ /* One of these must exist */
+ g_module_symbol (module, "f0r_update", (gpointer *) & ftable.update);
+ g_module_symbol (module, "f0r_update2", (gpointer *) & ftable.update2);
+
+ if (!ftable.init ()) {
+ GST_WARNING ("Failed to initialize plugin");
+ g_module_close (module);
+ return FALSE;
+ }
+
+ if (!ftable.update && !ftable.update2)
+ goto invalid_frei0r_plugin;
+
+ ftable.get_plugin_info (&info);
+
+ if (info.frei0r_version > 1) {
+ GST_WARNING ("Unsupported frei0r version %d", info.frei0r_version);
+ ftable.deinit ();
+ g_module_close (module);
+ return FALSE;
+ }
+
+ if (info.color_model > F0R_COLOR_MODEL_PACKED32) {
+ GST_WARNING ("Unsupported color model %d", info.color_model);
+ ftable.deinit ();
+ g_module_close (module);
+ return FALSE;
+ }
+
+ for (i = 0; i < info.num_params; i++) {
+ f0r_param_info_t pinfo = { NULL, };
+
+ ftable.get_param_info (&pinfo, i);
+ if (pinfo.type > F0R_PARAM_STRING) {
+ GST_WARNING ("Unsupported parameter type %d", pinfo.type);
+ ftable.deinit ();
+ g_module_close (module);
+ return FALSE;
+ }
+ }
+
+ instance = ftable.construct (640, 480);
+ if (!instance) {
+ GST_WARNING ("Failed to instanciate plugin '%s'", info.name);
+ ftable.deinit ();
+ g_module_close (module);
+ return FALSE;
+ }
+ ftable.destruct (instance);
+
+ switch (info.plugin_type) {
+ case F0R_PLUGIN_TYPE_FILTER:
+ ret = gst_frei0r_filter_register (plugin, &info, &ftable);
+ break;
+ case F0R_PLUGIN_TYPE_SOURCE:
+ ret = gst_frei0r_src_register (plugin, &info, &ftable);
+ break;
+ case F0R_PLUGIN_TYPE_MIXER2:
+ case F0R_PLUGIN_TYPE_MIXER3:
+ ret = gst_frei0r_mixer_register (plugin, &info, &ftable);
+ break;
+ default:
+ break;
+ }
+
+ if (!ret)
+ goto invalid_frei0r_plugin;
+
+ return ret;
+
+invalid_frei0r_plugin:
+ GST_ERROR ("Invalid frei0r plugin");
+ ftable.deinit ();
+ g_module_close (module);
+
+ return FALSE;
+}
+
+static gboolean
+register_plugins (GstPlugin * plugin, const gchar * path)
+{
+ GDir *dir;
+ gchar *filename;
+ const gchar *entry_name;
+ gboolean ret = FALSE;
+
+ GST_DEBUG ("Scanning director '%s' for frei0r plugins", path);
+
+ dir = g_dir_open (path, 0, NULL);
+ if (!dir)
+ return FALSE;
+
+ while ((entry_name = g_dir_read_name (dir))) {
+ filename = g_build_filename (path, entry_name, NULL);
+ if ((g_str_has_suffix (filename, G_MODULE_SUFFIX)
+#ifdef GST_EXTRA_MODULE_SUFFIX
+ || g_str_has_suffix (filename, GST_EXTRA_MODULE_SUFFIX)
+#endif
+ ) && g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
+ ret |= register_plugin (plugin, filename);
+ } else if (g_file_test (filename, G_FILE_TEST_IS_DIR)) {
+ ret |= register_plugins (plugin, filename);
+ }
+ g_free (filename);
+ }
+ g_dir_close (dir);
+
+ return ret;
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ const gchar *homedir;
+ gchar *path;
+
+ GST_DEBUG_CATEGORY_INIT (frei0r_debug, "frei0r", 0, "frei0r");
+
+ gst_plugin_add_dependency_simple (plugin,
+ "HOME/.frei0r-1/lib",
+ "/usr/lib/frei0r-1:/usr/local/lib/frei0r-1",
+ NULL, GST_PLUGIN_DEPENDENCY_FLAG_RECURSE);
+
+ register_plugins (plugin, "/usr/lib/frei0r-1");
+ register_plugins (plugin, "/usr/local/lib/frei0r-1");
+
+ homedir = g_get_home_dir ();
+ path = g_build_filename (homedir, ".frei0r-1", NULL);
+ register_plugins (plugin, path);
+ g_free (path);
+
+ return TRUE;
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "frei0r",
+ "frei0r plugin library",
+ plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/gst/frei0r/gstfrei0r.h b/gst/frei0r/gstfrei0r.h
new file mode 100644
index 00000000..91e5e097
--- /dev/null
+++ b/gst/frei0r/gstfrei0r.h
@@ -0,0 +1,91 @@
+/* GStreamer
+ * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * 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_FREI0R_H__
+#define __GST_FREI0R_H__
+
+#include <gst/gst.h>
+
+#include "frei0r.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GstFrei0rFuncTable GstFrei0rFuncTable;
+typedef struct _GstFrei0rProperty GstFrei0rProperty;
+typedef struct _GstFrei0rPropertyValue GstFrei0rPropertyValue;
+
+struct _GstFrei0rPropertyValue {
+ union {
+ gboolean b;
+ gdouble d;
+ gchar *s;
+ f0r_param_position_t position;
+ f0r_param_color_t color;
+ } data;
+};
+
+struct _GstFrei0rProperty {
+ guint prop_id;
+ guint n_prop_ids;
+
+ gint prop_idx;
+ f0r_param_info_t info;
+
+ GstFrei0rPropertyValue default_value;
+};
+
+struct _GstFrei0rFuncTable {
+ int (*init) (void);
+ void (*deinit) (void);
+
+ f0r_instance_t (*construct) (unsigned int width, unsigned int height);
+ void (*destruct) (f0r_instance_t instance);
+
+ void (*get_plugin_info) (f0r_plugin_info_t* info);
+ void (*get_param_info) (f0r_param_info_t* info, int param_index);
+
+ void (*set_param_value) (f0r_instance_t instance,
+ f0r_param_t param, int param_index);
+ void (*get_param_value) (f0r_instance_t instance,
+ f0r_param_t param, int param_index);
+
+ void (*update) (f0r_instance_t instance,
+ double time, const uint32_t* inframe, uint32_t* outframe);
+ void (*update2) (f0r_instance_t instance,
+ double time,
+ const uint32_t* inframe1,
+ const uint32_t* inframe2,
+ const uint32_t* inframe3,
+ uint32_t* outframe);
+};
+
+void gst_frei0r_klass_install_properties (GObjectClass *gobject_class, GstFrei0rFuncTable *ftable, GstFrei0rProperty *properties, gint n_properties);
+
+f0r_instance_t * gst_frei0r_instance_construct (GstFrei0rFuncTable *ftable, GstFrei0rProperty *properties, gint n_properties, GstFrei0rPropertyValue *property_cache, gint width, gint height);
+
+GstFrei0rPropertyValue * gst_frei0r_property_cache_init (GstFrei0rProperty *properties, gint n_properties);
+void gst_frei0r_property_cache_free (GstFrei0rProperty *properties, GstFrei0rPropertyValue *property_cache, gint n_properties);
+
+GstCaps * gst_frei0r_caps_from_color_model (gint color_model);
+gboolean gst_frei0r_get_property (f0r_instance_t *instance, GstFrei0rFuncTable *ftable, GstFrei0rProperty *properties, gint n_properties, GstFrei0rPropertyValue *property_cache, guint prop_id, GValue *value);
+gboolean gst_frei0r_set_property (f0r_instance_t *instance, GstFrei0rFuncTable *ftable, GstFrei0rProperty *properties, gint n_properties, GstFrei0rPropertyValue *property_cache, guint prop_id, const GValue *value);
+
+G_END_DECLS
+
+#endif /* __GST_FREI0R_H__ */
diff --git a/gst/frei0r/gstfrei0rfilter.c b/gst/frei0r/gstfrei0rfilter.c
new file mode 100644
index 00000000..43d8fc67
--- /dev/null
+++ b/gst/frei0r/gstfrei0rfilter.c
@@ -0,0 +1,241 @@
+/* GStreamer
+ * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * 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 <string.h>
+
+#include "gstfrei0r.h"
+#include "gstfrei0rfilter.h"
+
+GST_DEBUG_CATEGORY_EXTERN (frei0r_debug);
+#define GST_CAT_DEFAULT frei0r_debug
+
+typedef struct
+{
+ f0r_plugin_info_t info;
+ GstFrei0rFuncTable ftable;
+} GstFrei0rFilterClassData;
+
+static gboolean
+gst_frei0r_filter_set_caps (GstBaseTransform * trans, GstCaps * incaps,
+ GstCaps * outcaps)
+{
+ GstFrei0rFilter *self = GST_FREI0R_FILTER (trans);
+ GstFrei0rFilterClass *klass = GST_FREI0R_FILTER_GET_CLASS (trans);
+ GstVideoFormat fmt;
+ gint width, height;
+
+ if (!gst_video_format_parse_caps (incaps, &fmt, &width, &height))
+ return FALSE;
+
+ if (self->f0r_instance) {
+ klass->ftable->destruct (self->f0r_instance);
+ self->f0r_instance = NULL;
+ }
+
+ self->f0r_instance =
+ gst_frei0r_instance_construct (klass->ftable, klass->properties,
+ klass->n_properties, self->property_cache, width, height);
+
+ return TRUE;
+}
+
+static gboolean
+gst_frei0r_filter_stop (GstBaseTransform * trans)
+{
+ GstFrei0rFilter *self = GST_FREI0R_FILTER (trans);
+ GstFrei0rFilterClass *klass = GST_FREI0R_FILTER_GET_CLASS (trans);
+
+ if (self->f0r_instance) {
+ klass->ftable->destruct (self->f0r_instance);
+ self->f0r_instance = NULL;
+ }
+
+ return TRUE;
+}
+
+static GstFlowReturn
+gst_frei0r_filter_transform (GstBaseTransform * trans, GstBuffer * inbuf,
+ GstBuffer * outbuf)
+{
+ GstFrei0rFilter *self = GST_FREI0R_FILTER (trans);
+ GstFrei0rFilterClass *klass = GST_FREI0R_FILTER_GET_CLASS (trans);
+ gdouble time;
+
+ if (!self->f0r_instance)
+ return GST_FLOW_NOT_NEGOTIATED;
+
+ time = ((gdouble) GST_BUFFER_TIMESTAMP (inbuf)) / GST_SECOND;
+
+ if (klass->ftable->update2)
+ klass->ftable->update2 (self->f0r_instance, time,
+ (const guint32 *) GST_BUFFER_DATA (inbuf), NULL, NULL,
+ (guint32 *) GST_BUFFER_DATA (outbuf));
+ else
+ klass->ftable->update (self->f0r_instance, time,
+ (const guint32 *) GST_BUFFER_DATA (inbuf),
+ (guint32 *) GST_BUFFER_DATA (outbuf));
+
+ return GST_FLOW_OK;
+}
+
+static void
+gst_frei0r_filter_finalize (GObject * object)
+{
+ GstFrei0rFilter *self = GST_FREI0R_FILTER (object);
+ GstFrei0rFilterClass *klass = GST_FREI0R_FILTER_GET_CLASS (object);
+
+ if (self->f0r_instance) {
+ klass->ftable->destruct (self->f0r_instance);
+ self->f0r_instance = NULL;
+ }
+
+ if (self->property_cache)
+ gst_frei0r_property_cache_free (klass->properties, self->property_cache,
+ klass->n_properties);
+ self->property_cache = NULL;
+
+ G_OBJECT_CLASS (g_type_class_peek_parent (klass))->finalize (object);
+}
+
+static void
+gst_frei0r_filter_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ GstFrei0rFilter *self = GST_FREI0R_FILTER (object);
+ GstFrei0rFilterClass *klass = GST_FREI0R_FILTER_GET_CLASS (object);
+
+ if (!gst_frei0r_get_property (self->f0r_instance, klass->ftable,
+ klass->properties, klass->n_properties, self->property_cache, prop_id,
+ value))
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+gst_frei0r_filter_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstFrei0rFilter *self = GST_FREI0R_FILTER (object);
+ GstFrei0rFilterClass *klass = GST_FREI0R_FILTER_GET_CLASS (object);
+
+ if (!gst_frei0r_set_property (self->f0r_instance, klass->ftable,
+ klass->properties, klass->n_properties, self->property_cache, prop_id,
+ value))
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+gst_frei0r_filter_class_init (GstFrei0rFilterClass * klass,
+ GstFrei0rFilterClassData * class_data)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+ GstElementClass *gstelement_class = (GstElementClass *) klass;
+ GstBaseTransformClass *gsttrans_class = (GstBaseTransformClass *) klass;
+ GstPadTemplate *templ;
+ GstCaps *caps;
+ gchar *author;
+
+ klass->ftable = &class_data->ftable;
+ klass->info = &class_data->info;
+
+ gobject_class->finalize = gst_frei0r_filter_finalize;
+ gobject_class->set_property = gst_frei0r_filter_set_property;
+ gobject_class->get_property = gst_frei0r_filter_get_property;
+
+ klass->n_properties = klass->info->num_params;
+ klass->properties = g_new0 (GstFrei0rProperty, klass->n_properties);
+
+ gst_frei0r_klass_install_properties (gobject_class, klass->ftable,
+ klass->properties, klass->n_properties);
+
+ author =
+ g_strdup_printf
+ ("Sebastian Dröge <sebastian.droege@collabora.co.uk>, %s",
+ class_data->info.author);
+ gst_element_class_set_details_simple (gstelement_class, class_data->info.name,
+ "Filter/Effect/Video", class_data->info.explanation, author);
+ g_free (author);
+
+ caps = gst_frei0r_caps_from_color_model (class_data->info.color_model);
+
+ templ =
+ gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
+ gst_caps_ref (caps));
+ gst_element_class_add_pad_template (gstelement_class, templ);
+
+ templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps);
+ gst_element_class_add_pad_template (gstelement_class, templ);
+
+ gsttrans_class->set_caps = GST_DEBUG_FUNCPTR (gst_frei0r_filter_set_caps);
+ gsttrans_class->stop = GST_DEBUG_FUNCPTR (gst_frei0r_filter_stop);
+ gsttrans_class->transform = GST_DEBUG_FUNCPTR (gst_frei0r_filter_transform);
+}
+
+static void
+gst_frei0r_filter_init (GstFrei0rFilter * self, GstFrei0rFilterClass * klass)
+{
+ self->property_cache =
+ gst_frei0r_property_cache_init (klass->properties, klass->n_properties);
+}
+
+gboolean
+gst_frei0r_filter_register (GstPlugin * plugin, const f0r_plugin_info_t * info,
+ const GstFrei0rFuncTable * ftable)
+{
+ GTypeInfo typeinfo = {
+ sizeof (GstFrei0rFilterClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) gst_frei0r_filter_class_init,
+ NULL,
+ NULL,
+ sizeof (GstFrei0rFilter),
+ 0,
+ (GInstanceInitFunc) gst_frei0r_filter_init
+ };
+ GType type;
+ gchar *type_name, *tmp;
+ GstFrei0rFilterClassData *class_data;
+ gboolean ret = FALSE;
+
+ tmp = g_strdup_printf ("frei0r-filter-%s", info->name);
+ type_name = g_ascii_strdown (tmp, -1);
+ g_free (tmp);
+ g_strcanon (type_name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-+", '-');
+
+ if (g_type_from_name (type_name)) {
+ GST_WARNING ("Type '%s' already exists", type_name);
+ return FALSE;
+ }
+
+ class_data = g_new0 (GstFrei0rFilterClassData, 1);
+ memcpy (&class_data->info, info, sizeof (f0r_plugin_info_t));
+ memcpy (&class_data->ftable, ftable, sizeof (GstFrei0rFuncTable));
+ typeinfo.class_data = class_data;
+
+ type =
+ g_type_register_static (GST_TYPE_VIDEO_FILTER, type_name, &typeinfo, 0);
+ ret = gst_element_register (plugin, type_name, GST_RANK_NONE, type);
+
+ g_free (type_name);
+ return ret;
+}
diff --git a/gst/frei0r/gstfrei0rfilter.h b/gst/frei0r/gstfrei0rfilter.h
new file mode 100644
index 00000000..ded2172b
--- /dev/null
+++ b/gst/frei0r/gstfrei0rfilter.h
@@ -0,0 +1,63 @@
+/* GStreamer
+ * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * 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_FREI0R_FILTER_H__
+#define __GST_FREI0R_FILTER_H__
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <gst/video/gstvideofilter.h>
+
+#include "frei0r.h"
+#include "gstfrei0r.h"
+
+G_BEGIN_DECLS
+
+#define GST_FREI0R_FILTER(obj) \
+ ((GstFrei0rFilter *) obj)
+#define GST_FREI0R_FILTER_CLASS(klass) \
+ ((GstFrei0rFilterClass *) klass)
+#define GST_FREI0R_FILTER_GET_CLASS(obj) \
+ ((GstFrei0rFilterClass *) g_type_class_peek (G_TYPE_FROM_INSTANCE (obj)))
+
+typedef struct _GstFrei0rFilter GstFrei0rFilter;
+typedef struct _GstFrei0rFilterClass GstFrei0rFilterClass;
+
+struct _GstFrei0rFilter {
+ GstVideoFilter parent;
+
+ f0r_instance_t *f0r_instance;
+ GstFrei0rPropertyValue *property_cache;
+};
+
+struct _GstFrei0rFilterClass {
+ GstVideoFilterClass parent;
+
+ f0r_plugin_info_t *info;
+ GstFrei0rFuncTable *ftable;
+
+ GstFrei0rProperty *properties;
+ gint n_properties;
+};
+
+gboolean gst_frei0r_filter_register (GstPlugin *plugin, const f0r_plugin_info_t *info, const GstFrei0rFuncTable *ftable);
+
+G_END_DECLS
+
+#endif /* __GST_FREI0R_FILTER_H__ */
diff --git a/gst/frei0r/gstfrei0rmixer.c b/gst/frei0r/gstfrei0rmixer.c
new file mode 100644
index 00000000..745c330e
--- /dev/null
+++ b/gst/frei0r/gstfrei0rmixer.c
@@ -0,0 +1,782 @@
+/* GStreamer
+ * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * 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 <string.h>
+
+#include "gstfrei0r.h"
+#include "gstfrei0rmixer.h"
+
+GST_DEBUG_CATEGORY_EXTERN (frei0r_debug);
+#define GST_CAT_DEFAULT frei0r_debug
+
+typedef struct
+{
+ f0r_plugin_info_t info;
+ GstFrei0rFuncTable ftable;
+} GstFrei0rMixerClassData;
+
+static void
+gst_frei0r_mixer_reset (GstFrei0rMixer * self)
+{
+ GstFrei0rMixerClass *klass = GST_FREI0R_MIXER_GET_CLASS (self);
+
+ if (self->f0r_instance) {
+ klass->ftable->destruct (self->f0r_instance);
+ self->f0r_instance = NULL;
+ }
+
+ gst_caps_replace (&self->caps, NULL);
+ gst_event_replace (&self->newseg_event, NULL);
+}
+
+static void
+gst_frei0r_mixer_finalize (GObject * object)
+{
+ GstFrei0rMixer *self = GST_FREI0R_MIXER (object);
+ GstFrei0rMixerClass *klass = GST_FREI0R_MIXER_GET_CLASS (object);
+
+ if (self->property_cache)
+ gst_frei0r_property_cache_free (klass->properties, self->property_cache,
+ klass->n_properties);
+ self->property_cache = NULL;
+
+ if (self->collect)
+ gst_object_unref (self->collect);
+ self->collect = NULL;
+
+ G_OBJECT_CLASS (g_type_class_peek_parent (klass))->finalize (object);
+}
+
+static void
+gst_frei0r_mixer_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ GstFrei0rMixer *self = GST_FREI0R_MIXER (object);
+ GstFrei0rMixerClass *klass = GST_FREI0R_MIXER_GET_CLASS (object);
+
+ if (!gst_frei0r_get_property (self->f0r_instance, klass->ftable,
+ klass->properties, klass->n_properties, self->property_cache, prop_id,
+ value))
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+gst_frei0r_mixer_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstFrei0rMixer *self = GST_FREI0R_MIXER (object);
+ GstFrei0rMixerClass *klass = GST_FREI0R_MIXER_GET_CLASS (object);
+
+ if (!gst_frei0r_set_property (self->f0r_instance, klass->ftable,
+ klass->properties, klass->n_properties, self->property_cache, prop_id,
+ value))
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static GstStateChangeReturn
+gst_frei0r_mixer_change_state (GstElement * element, GstStateChange transition)
+{
+ GstFrei0rMixer *self = GST_FREI0R_MIXER (element);
+ GstFrei0rMixerClass *klass = GST_FREI0R_MIXER_GET_CLASS (self);
+ GstStateChangeReturn ret;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
+ break;
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ gst_collect_pads_start (self->collect);
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ break;
+ default:
+ break;
+ }
+
+ /* Stop before calling the parent's state change function as
+ * GstCollectPads might take locks and we would deadlock in that
+ * case
+ */
+ if (transition == GST_STATE_CHANGE_PAUSED_TO_READY)
+ gst_collect_pads_stop (self->collect);
+
+ ret =
+ GST_ELEMENT_CLASS (g_type_class_peek_parent (klass))->change_state
+ (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ gst_frei0r_mixer_reset (self);
+ break;
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static GstCaps *
+gst_frei0r_mixer_get_caps (GstPad * pad)
+{
+ GstFrei0rMixer *self = GST_FREI0R_MIXER (gst_pad_get_parent (pad));
+ GstCaps *caps = NULL;
+
+ if (self->caps) {
+ caps = gst_caps_ref (self->caps);
+ } else {
+ GstCaps *tmp, *tmp1;
+
+ tmp = gst_caps_copy (gst_pad_get_pad_template_caps (self->src));
+ tmp1 = gst_pad_peer_get_caps (pad);
+ if (tmp1) {
+ caps = gst_caps_intersect (tmp, tmp1);
+ gst_caps_unref (tmp1);
+ gst_caps_unref (tmp);
+ } else {
+ caps = tmp;
+ }
+
+ tmp = caps;
+ tmp1 = gst_pad_peer_get_caps (self->sink0);
+ if (tmp1) {
+ caps = gst_caps_intersect (tmp, tmp1);
+ gst_caps_unref (tmp);
+ gst_caps_unref (tmp1);
+ }
+
+ tmp = caps;
+ tmp1 = gst_pad_peer_get_caps (self->sink1);
+ if (tmp1) {
+ caps = gst_caps_intersect (tmp, tmp1);
+ gst_caps_unref (tmp);
+ gst_caps_unref (tmp1);
+ }
+
+ if (self->sink2) {
+ tmp = caps;
+ tmp1 = gst_pad_peer_get_caps (self->sink2);
+ if (tmp1) {
+ caps = gst_caps_intersect (tmp, tmp1);
+ gst_caps_unref (tmp);
+ gst_caps_unref (tmp1);
+ }
+ }
+ }
+
+ gst_object_unref (self);
+
+ return caps;
+}
+
+static gboolean
+gst_frei0r_mixer_set_caps (GstPad * pad, GstCaps * caps)
+{
+ GstFrei0rMixer *self = GST_FREI0R_MIXER (gst_pad_get_parent (pad));
+ GstFrei0rMixerClass *klass = GST_FREI0R_MIXER_GET_CLASS (self);
+ gboolean ret = TRUE;
+
+ gst_caps_replace (&self->caps, caps);
+
+ if (pad != self->src)
+ ret &= gst_pad_set_caps (self->src, caps);
+ if (pad != self->sink0)
+ ret &= gst_pad_set_caps (self->sink0, caps);
+ if (pad != self->sink1)
+ ret &= gst_pad_set_caps (self->sink1, caps);
+ if (pad != self->sink2 && self->sink2)
+ ret &= gst_pad_set_caps (self->sink2, caps);
+
+ if (ret) {
+ if (!gst_video_format_parse_caps (caps, &self->fmt, &self->width,
+ &self->height)) {
+ ret = FALSE;
+ goto out;
+ }
+
+ if (self->f0r_instance) {
+ klass->ftable->destruct (self->f0r_instance);
+ self->f0r_instance = NULL;
+ }
+
+ self->f0r_instance =
+ gst_frei0r_instance_construct (klass->ftable, klass->properties,
+ klass->n_properties, self->property_cache, self->width, self->height);
+
+ }
+out:
+
+ gst_object_unref (self);
+
+ return ret;
+}
+
+static gboolean
+gst_frei0r_mixer_src_query_duration (GstFrei0rMixer * self, GstQuery * query)
+{
+ gint64 min;
+ gboolean res;
+ GstFormat format;
+ GstIterator *it;
+ gboolean done;
+
+ /* parse format */
+ gst_query_parse_duration (query, &format, NULL);
+
+ min = -1;
+ res = TRUE;
+ done = FALSE;
+
+ /* Take minimum of all durations */
+ it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (self));
+ while (!done) {
+ GstIteratorResult ires;
+ gpointer item;
+
+ ires = gst_iterator_next (it, &item);
+ switch (ires) {
+ case GST_ITERATOR_DONE:
+ done = TRUE;
+ break;
+ case GST_ITERATOR_OK:
+ {
+ GstPad *pad = GST_PAD_CAST (item);
+ gint64 duration;
+
+ /* ask sink peer for duration */
+ res &= gst_pad_query_peer_duration (pad, &format, &duration);
+ /* take min from all valid return values */
+ if (res) {
+ /* valid unknown length, stop searching */
+ if (duration == -1) {
+ min = duration;
+ done = TRUE;
+ }
+ /* else see if smaller than current min */
+ else if (duration < min)
+ min = duration;
+ }
+ gst_object_unref (pad);
+ break;
+ }
+ case GST_ITERATOR_RESYNC:
+ min = -1;
+ res = TRUE;
+ gst_iterator_resync (it);
+ break;
+ default:
+ res = FALSE;
+ done = TRUE;
+ break;
+ }
+ }
+ gst_iterator_free (it);
+
+ if (res) {
+ /* and store the min */
+ GST_DEBUG_OBJECT (self, "Total duration in format %s: %"
+ GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (min));
+ gst_query_set_duration (query, format, min);
+ }
+
+ return res;
+}
+
+static gboolean
+gst_frei0r_mixer_src_query_latency (GstFrei0rMixer * self, GstQuery * query)
+{
+ GstClockTime min, max;
+ gboolean live;
+ gboolean res;
+ GstIterator *it;
+ gboolean done;
+
+ res = TRUE;
+ done = FALSE;
+
+ live = FALSE;
+ min = 0;
+ max = GST_CLOCK_TIME_NONE;
+
+ /* Take maximum of all latency values */
+ it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (self));
+ while (!done) {
+ GstIteratorResult ires;
+ gpointer item;
+
+ ires = gst_iterator_next (it, &item);
+ switch (ires) {
+ case GST_ITERATOR_DONE:
+ done = TRUE;
+ break;
+ case GST_ITERATOR_OK:
+ {
+ GstPad *pad = GST_PAD_CAST (item);
+ GstQuery *peerquery;
+ GstClockTime min_cur, max_cur;
+ gboolean live_cur;
+
+ peerquery = gst_query_new_latency ();
+
+ /* Ask peer for latency */
+ res &= gst_pad_peer_query (pad, peerquery);
+
+ /* take max from all valid return values */
+ if (res) {
+ gst_query_parse_latency (peerquery, &live_cur, &min_cur, &max_cur);
+
+ if (min_cur > min)
+ min = min_cur;
+
+ if (max_cur != GST_CLOCK_TIME_NONE &&
+ ((max != GST_CLOCK_TIME_NONE && max_cur > max) ||
+ (max == GST_CLOCK_TIME_NONE)))
+ max = max_cur;
+
+ live = live || live_cur;
+ }
+
+ gst_query_unref (peerquery);
+ gst_object_unref (pad);
+ break;
+ }
+ case GST_ITERATOR_RESYNC:
+ live = FALSE;
+ min = 0;
+ max = GST_CLOCK_TIME_NONE;
+ res = TRUE;
+ gst_iterator_resync (it);
+ break;
+ default:
+ res = FALSE;
+ done = TRUE;
+ break;
+ }
+ }
+ gst_iterator_free (it);
+
+ if (res) {
+ /* store the results */
+ GST_DEBUG_OBJECT (self, "Calculated total latency: live %s, min %"
+ GST_TIME_FORMAT ", max %" GST_TIME_FORMAT,
+ (live ? "yes" : "no"), GST_TIME_ARGS (min), GST_TIME_ARGS (max));
+ gst_query_set_latency (query, live, min, max);
+ }
+
+ return res;
+}
+
+static gboolean
+gst_frei0r_mixer_src_query (GstPad * pad, GstQuery * query)
+{
+ GstFrei0rMixer *self = GST_FREI0R_MIXER (gst_pad_get_parent (pad));
+ gboolean ret = FALSE;
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_POSITION:
+ ret = gst_pad_query (self->sink0, query);
+ break;
+ case GST_QUERY_DURATION:
+ ret = gst_frei0r_mixer_src_query_duration (self, query);
+ break;
+ case GST_QUERY_LATENCY:
+ ret = gst_frei0r_mixer_src_query_latency (self, query);
+ break;
+ default:
+ ret = gst_pad_query_default (pad, query);
+ break;
+ }
+
+ gst_object_unref (self);
+
+ return ret;
+}
+
+static gboolean
+gst_frei0r_mixer_sink_query (GstPad * pad, GstQuery * query)
+{
+ GstFrei0rMixer *self = GST_FREI0R_MIXER (gst_pad_get_parent (pad));
+ gboolean ret = gst_pad_query (self->src, query);
+
+ gst_object_unref (self);
+
+ return ret;
+}
+
+static gboolean
+forward_event_func (GstPad * pad, GValue * ret, GstEvent * event)
+{
+ gst_event_ref (event);
+ GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event));
+ if (!gst_pad_push_event (pad, event)) {
+ g_value_set_boolean (ret, FALSE);
+ GST_WARNING_OBJECT (pad, "Sending event %p (%s) failed.",
+ event, GST_EVENT_TYPE_NAME (event));
+ } else {
+ GST_LOG_OBJECT (pad, "Sent event %p (%s).",
+ event, GST_EVENT_TYPE_NAME (event));
+ }
+ gst_object_unref (pad);
+ return TRUE;
+}
+
+static gboolean
+forward_event (GstFrei0rMixer * self, GstEvent * event)
+{
+ GstIterator *it;
+ GValue vret = { 0 };
+
+ GST_LOG_OBJECT (self, "Forwarding event %p (%s)", event,
+ GST_EVENT_TYPE_NAME (event));
+
+ g_value_init (&vret, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&vret, TRUE);
+ it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (self));
+ gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func, &vret,
+ event);
+ gst_iterator_free (it);
+ gst_event_unref (event);
+
+ return g_value_get_boolean (&vret);
+}
+
+static gboolean
+gst_frei0r_mixer_src_event (GstPad * pad, GstEvent * event)
+{
+ GstFrei0rMixer *self = GST_FREI0R_MIXER (gst_pad_get_parent (pad));
+ gboolean ret = FALSE;
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_QOS:
+ /* QoS might be tricky */
+ ret = FALSE;
+ break;
+ case GST_EVENT_SEEK:
+ {
+ GstSeekFlags flags;
+
+ /* parse the seek parameters */
+ gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
+
+ /* check if we are flushing */
+ if (flags & GST_SEEK_FLAG_FLUSH) {
+ /* make sure we accept nothing anymore and return WRONG_STATE */
+ gst_collect_pads_set_flushing (self->collect, TRUE);
+
+ /* flushing seek, start flush downstream, the flush will be done
+ * when all pads received a FLUSH_STOP. */
+ gst_pad_push_event (self->src, gst_event_new_flush_start ());
+ }
+
+ ret = forward_event (self, event);
+ break;
+ }
+ case GST_EVENT_NAVIGATION:
+ /* navigation is rather pointless. */
+ ret = FALSE;
+ break;
+ default:
+ /* just forward the rest for now */
+ ret = forward_event (self, event);
+ break;
+ }
+
+ gst_object_unref (self);
+
+ return ret;
+}
+
+static gboolean
+gst_frei0r_mixer_sink0_event (GstPad * pad, GstEvent * event)
+{
+ GstFrei0rMixer *self = GST_FREI0R_MIXER (gst_pad_get_parent (pad));
+ gboolean ret = FALSE;
+
+ GST_DEBUG ("Got %s event on pad %s:%s", GST_EVENT_TYPE_NAME (event),
+ GST_DEBUG_PAD_NAME (pad));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_NEWSEGMENT:
+ gst_event_replace (&self->newseg_event, event);
+ break;
+ default:
+ break;
+ }
+
+ /* now GstCollectPads can take care of the rest, e.g. EOS */
+ ret = self->collect_event (pad, event);
+
+ gst_object_unref (self);
+
+ return ret;
+}
+
+static GstFlowReturn
+gst_frei0r_mixer_collected (GstCollectPads * pads, GstFrei0rMixer * self)
+{
+ GstBuffer *inbuf0 = NULL, *inbuf1 = NULL, *inbuf2 = NULL;
+ GstBuffer *outbuf = NULL;
+ GstFlowReturn ret = GST_FLOW_OK;
+ GSList *l;
+ GstFrei0rMixerClass *klass = GST_FREI0R_MIXER_GET_CLASS (self);
+ gdouble time;
+
+ if (G_UNLIKELY (!self->f0r_instance))
+ return GST_FLOW_NOT_NEGOTIATED;
+
+ if (self->newseg_event) {
+ gst_pad_push_event (self->src, self->newseg_event);
+ self->newseg_event = NULL;
+ }
+
+ if ((ret =
+ gst_pad_alloc_buffer_and_set_caps (self->src, GST_BUFFER_OFFSET_NONE,
+ gst_video_format_get_size (self->fmt, self->width, self->height),
+ GST_PAD_CAPS (self->src), &outbuf)) != GST_FLOW_OK)
+ return ret;
+
+ for (l = pads->data; l; l = l->next) {
+ GstCollectData *cdata = l->data;
+
+ if (cdata->pad == self->sink0)
+ inbuf0 = gst_collect_pads_pop (pads, cdata);
+ else if (cdata->pad == self->sink1)
+ inbuf1 = gst_collect_pads_pop (pads, cdata);
+ else if (cdata->pad == self->sink2)
+ inbuf2 = gst_collect_pads_pop (pads, cdata);
+ }
+
+ if (!inbuf0 || !inbuf1 || (!inbuf2 && self->sink2))
+ goto eos;
+
+ gst_buffer_copy_metadata (outbuf, inbuf0,
+ GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS);
+ time = ((gdouble) GST_BUFFER_TIMESTAMP (outbuf)) / GST_SECOND;
+
+ klass->ftable->update2 (self->f0r_instance, time,
+ (const guint32 *) GST_BUFFER_DATA (inbuf0),
+ (const guint32 *) GST_BUFFER_DATA (inbuf1),
+ (inbuf2) ? (const guint32 *) GST_BUFFER_DATA (inbuf2) : NULL,
+ (guint32 *) GST_BUFFER_DATA (outbuf));
+
+ gst_buffer_unref (inbuf0);
+ gst_buffer_unref (inbuf1);
+ if (inbuf2)
+ gst_buffer_unref (inbuf2);
+
+ ret = gst_pad_push (self->src, outbuf);
+
+ return ret;
+
+eos:
+ {
+ GST_DEBUG_OBJECT (self, "no data available, must be EOS");
+ gst_buffer_unref (outbuf);
+
+ if (inbuf0)
+ gst_buffer_unref (inbuf0);
+ if (inbuf1)
+ gst_buffer_unref (inbuf1);
+ if (inbuf2)
+ gst_buffer_unref (inbuf2);
+
+ gst_pad_push_event (self->src, gst_event_new_eos ());
+ return GST_FLOW_UNEXPECTED;
+ }
+}
+
+static void
+gst_frei0r_mixer_class_init (GstFrei0rMixerClass * klass,
+ GstFrei0rMixerClassData * class_data)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+ GstElementClass *gstelement_class = (GstElementClass *) klass;
+ GstPadTemplate *templ;
+ GstCaps *caps;
+ gchar *author;
+
+ klass->ftable = &class_data->ftable;
+ klass->info = &class_data->info;
+
+ gobject_class->finalize = gst_frei0r_mixer_finalize;
+ gobject_class->set_property = gst_frei0r_mixer_set_property;
+ gobject_class->get_property = gst_frei0r_mixer_get_property;
+
+ klass->n_properties = klass->info->num_params;
+ klass->properties = g_new0 (GstFrei0rProperty, klass->n_properties);
+
+ gst_frei0r_klass_install_properties (gobject_class, klass->ftable,
+ klass->properties, klass->n_properties);
+
+ author =
+ g_strdup_printf
+ ("Sebastian Dröge <sebastian.droege@collabora.co.uk>, %s",
+ class_data->info.author);
+ gst_element_class_set_details_simple (gstelement_class, class_data->info.name,
+ "Filter/Editor/Video", class_data->info.explanation, author);
+ g_free (author);
+
+ caps = gst_frei0r_caps_from_color_model (class_data->info.color_model);
+
+ templ =
+ gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
+ gst_caps_ref (caps));
+ gst_element_class_add_pad_template (gstelement_class, templ);
+
+ templ =
+ gst_pad_template_new ("sink_0", GST_PAD_SINK, GST_PAD_ALWAYS,
+ gst_caps_ref (caps));
+ gst_element_class_add_pad_template (gstelement_class, templ);
+
+ templ =
+ gst_pad_template_new ("sink_1", GST_PAD_SINK, GST_PAD_ALWAYS,
+ gst_caps_ref (caps));
+ gst_element_class_add_pad_template (gstelement_class, templ);
+
+ if (klass->info->plugin_type == F0R_PLUGIN_TYPE_MIXER3) {
+ templ =
+ gst_pad_template_new ("sink_2", GST_PAD_SINK, GST_PAD_ALWAYS,
+ gst_caps_ref (caps));
+ gst_element_class_add_pad_template (gstelement_class, templ);
+ }
+ gst_caps_unref (caps);
+
+ gstelement_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_frei0r_mixer_change_state);
+}
+
+static void
+gst_frei0r_mixer_init (GstFrei0rMixer * self, GstFrei0rMixerClass * klass)
+{
+ self->property_cache =
+ gst_frei0r_property_cache_init (klass->properties, klass->n_properties);
+
+ self->collect = gst_collect_pads_new ();
+ gst_collect_pads_set_function (self->collect,
+ (GstCollectPadsFunction) gst_frei0r_mixer_collected, self);
+
+ self->src =
+ gst_pad_new_from_template (gst_element_class_get_pad_template
+ (GST_ELEMENT_CLASS (klass), "src"), "src");
+ gst_pad_set_getcaps_function (self->src,
+ GST_DEBUG_FUNCPTR (gst_frei0r_mixer_get_caps));
+ gst_pad_set_setcaps_function (self->src,
+ GST_DEBUG_FUNCPTR (gst_frei0r_mixer_set_caps));
+ gst_pad_set_query_function (self->src,
+ GST_DEBUG_FUNCPTR (gst_frei0r_mixer_src_query));
+ gst_pad_set_event_function (self->src,
+ GST_DEBUG_FUNCPTR (gst_frei0r_mixer_src_event));
+ gst_element_add_pad (GST_ELEMENT_CAST (self), self->src);
+
+ self->sink0 =
+ gst_pad_new_from_template (gst_element_class_get_pad_template
+ (GST_ELEMENT_CLASS (klass), "sink_0"), "sink_0");
+ gst_pad_set_getcaps_function (self->sink0,
+ GST_DEBUG_FUNCPTR (gst_frei0r_mixer_get_caps));
+ gst_pad_set_setcaps_function (self->sink0,
+ GST_DEBUG_FUNCPTR (gst_frei0r_mixer_set_caps));
+ gst_pad_set_query_function (self->sink0,
+ GST_DEBUG_FUNCPTR (gst_frei0r_mixer_sink_query));
+ gst_collect_pads_add_pad (self->collect, self->sink0,
+ sizeof (GstCollectData));
+ self->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (self->sink0);
+ gst_pad_set_event_function (self->sink0,
+ GST_DEBUG_FUNCPTR (gst_frei0r_mixer_sink0_event));
+ gst_element_add_pad (GST_ELEMENT_CAST (self), self->sink0);
+
+ self->sink1 =
+ gst_pad_new_from_template (gst_element_class_get_pad_template
+ (GST_ELEMENT_CLASS (klass), "sink_1"), "sink_1");
+ gst_pad_set_getcaps_function (self->sink1,
+ GST_DEBUG_FUNCPTR (gst_frei0r_mixer_get_caps));
+ gst_pad_set_setcaps_function (self->sink1,
+ GST_DEBUG_FUNCPTR (gst_frei0r_mixer_set_caps));
+ gst_pad_set_query_function (self->sink0,
+ GST_DEBUG_FUNCPTR (gst_frei0r_mixer_sink_query));
+ gst_collect_pads_add_pad (self->collect, self->sink1,
+ sizeof (GstCollectData));
+ gst_element_add_pad (GST_ELEMENT_CAST (self), self->sink1);
+
+ if (klass->info->plugin_type == F0R_PLUGIN_TYPE_MIXER3) {
+ self->sink2 =
+ gst_pad_new_from_template (gst_element_class_get_pad_template
+ (GST_ELEMENT_CLASS (klass), "sink_2"), "sink_2");
+ gst_pad_set_getcaps_function (self->sink2,
+ GST_DEBUG_FUNCPTR (gst_frei0r_mixer_get_caps));
+ gst_pad_set_setcaps_function (self->sink2,
+ GST_DEBUG_FUNCPTR (gst_frei0r_mixer_set_caps));
+ gst_pad_set_query_function (self->sink0,
+ GST_DEBUG_FUNCPTR (gst_frei0r_mixer_sink_query));
+ gst_collect_pads_add_pad (self->collect, self->sink2,
+ sizeof (GstCollectData));
+ gst_element_add_pad (GST_ELEMENT_CAST (self), self->sink2);
+ }
+
+}
+
+gboolean
+gst_frei0r_mixer_register (GstPlugin * plugin, const f0r_plugin_info_t * info,
+ const GstFrei0rFuncTable * ftable)
+{
+ GTypeInfo typeinfo = {
+ sizeof (GstFrei0rMixerClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) gst_frei0r_mixer_class_init,
+ NULL,
+ NULL,
+ sizeof (GstFrei0rMixer),
+ 0,
+ (GInstanceInitFunc) gst_frei0r_mixer_init
+ };
+ GType type;
+ gchar *type_name, *tmp;
+ GstFrei0rMixerClassData *class_data;
+ gboolean ret = FALSE;
+
+ if (ftable->update2 == NULL)
+ return FALSE;
+
+ tmp = g_strdup_printf ("frei0r-mixer-%s", info->name);
+ type_name = g_ascii_strdown (tmp, -1);
+ g_free (tmp);
+ g_strcanon (type_name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-+", '-');
+
+ if (g_type_from_name (type_name)) {
+ GST_WARNING ("Type '%s' already exists", type_name);
+ return FALSE;
+ }
+
+ class_data = g_new0 (GstFrei0rMixerClassData, 1);
+ memcpy (&class_data->info, info, sizeof (f0r_plugin_info_t));
+ memcpy (&class_data->ftable, ftable, sizeof (GstFrei0rFuncTable));
+ typeinfo.class_data = class_data;
+
+ type = g_type_register_static (GST_TYPE_ELEMENT, type_name, &typeinfo, 0);
+ ret = gst_element_register (plugin, type_name, GST_RANK_NONE, type);
+
+ g_free (type_name);
+ return ret;
+}
diff --git a/gst/frei0r/gstfrei0rmixer.h b/gst/frei0r/gstfrei0rmixer.h
new file mode 100644
index 00000000..548b70ec
--- /dev/null
+++ b/gst/frei0r/gstfrei0rmixer.h
@@ -0,0 +1,75 @@
+/* GStreamer
+ * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * 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_FREI0R_MIXER_H__
+#define __GST_FREI0R_MIXER_H__
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <gst/base/gstcollectpads.h>
+
+#include "frei0r.h"
+#include "gstfrei0r.h"
+
+G_BEGIN_DECLS
+
+#define GST_FREI0R_MIXER(obj) \
+ ((GstFrei0rMixer *) obj)
+#define GST_FREI0R_MIXER_CLASS(klass) \
+ ((GstFrei0rMixerClass *) klass)
+#define GST_FREI0R_MIXER_GET_CLASS(obj) \
+ ((GstFrei0rMixerClass *) g_type_class_peek (G_TYPE_FROM_INSTANCE (obj)))
+
+typedef struct _GstFrei0rMixer GstFrei0rMixer;
+typedef struct _GstFrei0rMixerClass GstFrei0rMixerClass;
+
+struct _GstFrei0rMixer {
+ GstElement parent;
+
+ GstCollectPads *collect;
+ GstPad *src;
+ GstPad *sink0, *sink1, *sink2;
+
+ GstCaps *caps;
+ GstVideoFormat fmt;
+ gint width, height;
+
+ GstEvent *newseg_event;
+
+ GstPadEventFunction collect_event;
+
+ f0r_instance_t *f0r_instance;
+ GstFrei0rPropertyValue *property_cache;
+};
+
+struct _GstFrei0rMixerClass {
+ GstElementClass parent;
+
+ f0r_plugin_info_t *info;
+ GstFrei0rFuncTable *ftable;
+
+ GstFrei0rProperty *properties;
+ gint n_properties;
+};
+
+gboolean gst_frei0r_mixer_register (GstPlugin *plugin, const f0r_plugin_info_t *info, const GstFrei0rFuncTable *ftable);
+
+G_END_DECLS
+
+#endif /* __GST_FREI0R_MIXER_H__ */
diff --git a/gst/frei0r/gstfrei0rsrc.c b/gst/frei0r/gstfrei0rsrc.c
new file mode 100644
index 00000000..a713e1e9
--- /dev/null
+++ b/gst/frei0r/gstfrei0rsrc.c
@@ -0,0 +1,411 @@
+/* GStreamer
+ * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * 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 <string.h>
+
+#include "gstfrei0r.h"
+#include "gstfrei0rsrc.h"
+
+GST_DEBUG_CATEGORY_EXTERN (frei0r_debug);
+#define GST_CAT_DEFAULT frei0r_debug
+
+typedef struct
+{
+ f0r_plugin_info_t info;
+ GstFrei0rFuncTable ftable;
+} GstFrei0rSrcClassData;
+
+static gboolean
+gst_frei0r_src_set_caps (GstBaseSrc * src, GstCaps * caps)
+{
+ GstFrei0rSrc *self = GST_FREI0R_SRC (src);
+ GstFrei0rSrcClass *klass = GST_FREI0R_SRC_GET_CLASS (src);
+
+ if (!gst_video_format_parse_caps (caps, &self->fmt, &self->width,
+ &self->height)
+ || !gst_video_parse_caps_framerate (caps, &self->fps_n, &self->fps_d))
+ return FALSE;
+
+ if (self->f0r_instance) {
+ klass->ftable->destruct (self->f0r_instance);
+ self->f0r_instance = NULL;
+ }
+
+ self->f0r_instance =
+ gst_frei0r_instance_construct (klass->ftable, klass->properties,
+ klass->n_properties, self->property_cache, self->width, self->height);
+
+ return TRUE;
+}
+
+static GstCaps *
+gst_frei0r_src_get_caps (GstBaseSrc * src)
+{
+ if (GST_PAD_CAPS (GST_BASE_SRC_PAD (src)))
+ return gst_caps_ref (GST_PAD_CAPS (GST_BASE_SRC_PAD (src)));
+ else
+ return
+ gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (src)));
+}
+
+static GstFlowReturn
+gst_frei0r_src_create (GstPushSrc * src, GstBuffer ** buf)
+{
+ GstFrei0rSrc *self = GST_FREI0R_SRC (src);
+ GstFrei0rSrcClass *klass = GST_FREI0R_SRC_GET_CLASS (src);
+ guint size, newsize;
+ GstFlowReturn ret = GST_FLOW_OK;
+ GstBuffer *outbuf = NULL;
+ gdouble time;
+
+ *buf = NULL;
+
+ if (G_UNLIKELY (!self->f0r_instance))
+ return GST_FLOW_NOT_NEGOTIATED;
+
+ newsize = gst_video_format_get_size (self->fmt, self->width, self->height);
+
+ ret =
+ gst_pad_alloc_buffer_and_set_caps (GST_BASE_SRC_PAD (src),
+ GST_BUFFER_OFFSET_NONE, newsize, GST_PAD_CAPS (GST_BASE_SRC_PAD (src)),
+ &outbuf);
+ if (ret != GST_FLOW_OK)
+ return ret;
+
+ /* Format might have changed */
+ size = GST_BUFFER_SIZE (outbuf);
+ newsize = gst_video_format_get_size (self->fmt, self->width, self->height);
+
+ if (size != newsize) {
+ gst_buffer_unref (outbuf);
+ outbuf = gst_buffer_new_and_alloc (newsize);
+ gst_buffer_set_caps (outbuf, GST_PAD_CAPS (GST_BASE_SRC_PAD (src)));
+ }
+
+ GST_BUFFER_TIMESTAMP (outbuf) =
+ gst_util_uint64_scale (self->n_frames, GST_SECOND * self->fps_d,
+ self->fps_n);
+ GST_BUFFER_OFFSET (outbuf) = self->n_frames;
+ self->n_frames++;
+ GST_BUFFER_OFFSET_END (outbuf) = self->n_frames;
+ GST_BUFFER_DURATION (outbuf) =
+ gst_util_uint64_scale (self->n_frames, GST_SECOND * self->fps_d,
+ self->fps_n) - GST_BUFFER_TIMESTAMP (outbuf);
+
+ time = ((gdouble) GST_BUFFER_TIMESTAMP (outbuf)) / GST_SECOND;
+
+ if (klass->ftable->update2)
+ klass->ftable->update2 (self->f0r_instance, time, NULL, NULL, NULL,
+ (guint32 *) GST_BUFFER_DATA (outbuf));
+ else
+ klass->ftable->update (self->f0r_instance, time, NULL,
+ (guint32 *) GST_BUFFER_DATA (outbuf));
+
+ *buf = outbuf;
+
+ return GST_FLOW_OK;
+}
+
+static gboolean
+gst_frei0r_src_start (GstBaseSrc * basesrc)
+{
+ GstFrei0rSrc *self = GST_FREI0R_SRC (basesrc);
+
+ self->n_frames = 0;
+
+ return TRUE;
+}
+
+static gboolean
+gst_frei0r_src_stop (GstBaseSrc * basesrc)
+{
+ GstFrei0rSrc *self = GST_FREI0R_SRC (basesrc);
+ GstFrei0rSrcClass *klass = GST_FREI0R_SRC_GET_CLASS (basesrc);
+
+ if (self->f0r_instance) {
+ klass->ftable->destruct (self->f0r_instance);
+ self->f0r_instance = NULL;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_frei0r_src_is_seekable (GstBaseSrc * psrc)
+{
+ return TRUE;
+}
+
+static gboolean
+gst_frei0r_src_do_seek (GstBaseSrc * bsrc, GstSegment * segment)
+{
+ GstClockTime time;
+ GstFrei0rSrc *self = GST_FREI0R_SRC (bsrc);
+
+ segment->time = segment->start;
+ time = segment->last_stop;
+
+ /* now move to the time indicated */
+ if (self->fps_n) {
+ self->n_frames = gst_util_uint64_scale (time,
+ self->fps_n, self->fps_d * GST_SECOND);
+ } else {
+ self->n_frames = 0;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_frei0r_src_query (GstBaseSrc * bsrc, GstQuery * query)
+{
+ gboolean res;
+ GstFrei0rSrc *self = GST_FREI0R_SRC (bsrc);
+ GstFrei0rSrcClass *klass = GST_FREI0R_SRC_GET_CLASS (self);
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_CONVERT:
+ {
+ GstFormat src_fmt, dest_fmt;
+ gint64 src_val, dest_val;
+
+ gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
+ if (src_fmt == dest_fmt) {
+ dest_val = src_val;
+ goto done;
+ }
+
+ switch (src_fmt) {
+ case GST_FORMAT_DEFAULT:
+ switch (dest_fmt) {
+ case GST_FORMAT_TIME:
+ /* frames to time */
+ if (self->fps_n) {
+ dest_val = gst_util_uint64_scale (src_val,
+ self->fps_d * GST_SECOND, self->fps_n);
+ } else {
+ dest_val = 0;
+ }
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case GST_FORMAT_TIME:
+ switch (dest_fmt) {
+ case GST_FORMAT_DEFAULT:
+ /* time to frames */
+ if (self->fps_n) {
+ dest_val = gst_util_uint64_scale (src_val,
+ self->fps_n, self->fps_d * GST_SECOND);
+ } else {
+ dest_val = 0;
+ }
+ break;
+ default:
+ goto error;
+ }
+ break;
+ default:
+ goto error;
+ }
+ done:
+ gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
+ res = TRUE;
+ break;
+ }
+ default:
+ res =
+ GST_BASE_SRC_CLASS (g_type_class_peek_parent (klass))->query (bsrc,
+ query);
+ }
+ return res;
+
+ /* ERROR */
+error:
+ {
+ GST_DEBUG_OBJECT (self, "query failed");
+ return FALSE;
+ }
+}
+
+static void
+gst_frei0r_src_src_fixate (GstPad * pad, GstCaps * caps)
+{
+ GstStructure *structure;
+
+ structure = gst_caps_get_structure (caps, 0);
+
+ gst_structure_fixate_field_nearest_int (structure, "width", 320);
+ gst_structure_fixate_field_nearest_int (structure, "height", 240);
+ gst_structure_fixate_field_nearest_fraction (structure, "framerate", 30, 1);
+}
+
+static void
+gst_frei0r_src_finalize (GObject * object)
+{
+ GstFrei0rSrc *self = GST_FREI0R_SRC (object);
+ GstFrei0rSrcClass *klass = GST_FREI0R_SRC_GET_CLASS (object);
+
+ if (self->f0r_instance) {
+ klass->ftable->destruct (self->f0r_instance);
+ self->f0r_instance = NULL;
+ }
+
+ if (self->property_cache)
+ gst_frei0r_property_cache_free (klass->properties, self->property_cache,
+ klass->n_properties);
+ self->property_cache = NULL;
+
+ G_OBJECT_CLASS (g_type_class_peek_parent (klass))->finalize (object);
+}
+
+static void
+gst_frei0r_src_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ GstFrei0rSrc *self = GST_FREI0R_SRC (object);
+ GstFrei0rSrcClass *klass = GST_FREI0R_SRC_GET_CLASS (object);
+
+ if (!gst_frei0r_get_property (self->f0r_instance, klass->ftable,
+ klass->properties, klass->n_properties, self->property_cache, prop_id,
+ value))
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+gst_frei0r_src_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstFrei0rSrc *self = GST_FREI0R_SRC (object);
+ GstFrei0rSrcClass *klass = GST_FREI0R_SRC_GET_CLASS (object);
+
+ if (!gst_frei0r_set_property (self->f0r_instance, klass->ftable,
+ klass->properties, klass->n_properties, self->property_cache, prop_id,
+ value))
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+gst_frei0r_src_class_init (GstFrei0rSrcClass * klass,
+ GstFrei0rSrcClassData * class_data)
+{
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+ GstElementClass *gstelement_class = (GstElementClass *) klass;
+ GstPushSrcClass *gstpushsrc_class = (GstPushSrcClass *) klass;
+ GstBaseSrcClass *gstbasesrc_class = (GstBaseSrcClass *) klass;
+ GstPadTemplate *templ;
+ GstCaps *caps;
+ gchar *author;
+
+ klass->ftable = &class_data->ftable;
+ klass->info = &class_data->info;
+
+ gobject_class->finalize = gst_frei0r_src_finalize;
+ gobject_class->set_property = gst_frei0r_src_set_property;
+ gobject_class->get_property = gst_frei0r_src_get_property;
+
+ klass->n_properties = klass->info->num_params;
+ klass->properties = g_new0 (GstFrei0rProperty, klass->n_properties);
+
+ gst_frei0r_klass_install_properties (gobject_class, klass->ftable,
+ klass->properties, klass->n_properties);
+
+ author =
+ g_strdup_printf
+ ("Sebastian Dröge <sebastian.droege@collabora.co.uk>, %s",
+ class_data->info.author);
+ gst_element_class_set_details_simple (gstelement_class, class_data->info.name,
+ "Src/Video", class_data->info.explanation, author);
+ g_free (author);
+
+ caps = gst_frei0r_caps_from_color_model (class_data->info.color_model);
+
+ templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps);
+ gst_element_class_add_pad_template (gstelement_class, templ);
+
+ gstbasesrc_class->set_caps = gst_frei0r_src_set_caps;
+ gstbasesrc_class->get_caps = gst_frei0r_src_get_caps;
+ gstbasesrc_class->is_seekable = gst_frei0r_src_is_seekable;
+ gstbasesrc_class->do_seek = gst_frei0r_src_do_seek;
+ gstbasesrc_class->query = gst_frei0r_src_query;
+ gstbasesrc_class->start = gst_frei0r_src_start;
+ gstbasesrc_class->stop = gst_frei0r_src_stop;
+
+ gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_frei0r_src_create);
+}
+
+static void
+gst_frei0r_src_init (GstFrei0rSrc * self, GstFrei0rSrcClass * klass)
+{
+ GstPad *pad = GST_BASE_SRC_PAD (self);
+
+ self->property_cache =
+ gst_frei0r_property_cache_init (klass->properties, klass->n_properties);
+
+ gst_pad_set_fixatecaps_function (pad, gst_frei0r_src_src_fixate);
+
+ gst_base_src_set_format (GST_BASE_SRC_CAST (self), GST_FORMAT_TIME);
+}
+
+gboolean
+gst_frei0r_src_register (GstPlugin * plugin, const f0r_plugin_info_t * info,
+ const GstFrei0rFuncTable * ftable)
+{
+ GTypeInfo typeinfo = {
+ sizeof (GstFrei0rSrcClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) gst_frei0r_src_class_init,
+ NULL,
+ NULL,
+ sizeof (GstFrei0rSrc),
+ 0,
+ (GInstanceInitFunc) gst_frei0r_src_init
+ };
+ GType type;
+ gchar *type_name, *tmp;
+ GstFrei0rSrcClassData *class_data;
+ gboolean ret = FALSE;
+
+ tmp = g_strdup_printf ("frei0r-src-%s", info->name);
+ type_name = g_ascii_strdown (tmp, -1);
+ g_free (tmp);
+ g_strcanon (type_name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-+", '-');
+
+ if (g_type_from_name (type_name)) {
+ GST_WARNING ("Type '%s' already exists", type_name);
+ return FALSE;
+ }
+
+ class_data = g_new0 (GstFrei0rSrcClassData, 1);
+ memcpy (&class_data->info, info, sizeof (f0r_plugin_info_t));
+ memcpy (&class_data->ftable, ftable, sizeof (GstFrei0rFuncTable));
+ typeinfo.class_data = class_data;
+
+ type = g_type_register_static (GST_TYPE_PUSH_SRC, type_name, &typeinfo, 0);
+ ret = gst_element_register (plugin, type_name, GST_RANK_NONE, type);
+
+ g_free (type_name);
+ return ret;
+}
diff --git a/gst/frei0r/gstfrei0rsrc.h b/gst/frei0r/gstfrei0rsrc.h
new file mode 100644
index 00000000..291a644b
--- /dev/null
+++ b/gst/frei0r/gstfrei0rsrc.h
@@ -0,0 +1,69 @@
+/* GStreamer
+ * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * 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_FREI0R_SRC_H__
+#define __GST_FREI0R_SRC_H__
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <gst/base/gstpushsrc.h>
+
+#include "frei0r.h"
+#include "gstfrei0r.h"
+
+G_BEGIN_DECLS
+
+#define GST_FREI0R_SRC(obj) \
+ ((GstFrei0rSrc *) obj)
+#define GST_FREI0R_SRC_CLASS(klass) \
+ ((GstFrei0rSrcClass *) klass)
+#define GST_FREI0R_SRC_GET_CLASS(obj) \
+ ((GstFrei0rSrcClass *) g_type_class_peek (G_TYPE_FROM_INSTANCE (obj)))
+
+typedef struct _GstFrei0rSrc GstFrei0rSrc;
+typedef struct _GstFrei0rSrcClass GstFrei0rSrcClass;
+
+struct _GstFrei0rSrc {
+ GstPushSrc parent;
+
+ f0r_instance_t *f0r_instance;
+ GstFrei0rPropertyValue *property_cache;
+
+ GstVideoFormat fmt;
+ gint width, height;
+ gint fps_n, fps_d;
+
+ guint64 n_frames;
+};
+
+struct _GstFrei0rSrcClass {
+ GstPushSrcClass parent;
+
+ f0r_plugin_info_t *info;
+ GstFrei0rFuncTable *ftable;
+
+ GstFrei0rProperty *properties;
+ gint n_properties;
+};
+
+gboolean gst_frei0r_src_register (GstPlugin *plugin, const f0r_plugin_info_t *info, const GstFrei0rFuncTable *ftable);
+
+G_END_DECLS
+
+#endif /* __GST_FREI0R_SRC_H__ */
diff --git a/gst/hdvparse/Makefile.am b/gst/hdvparse/Makefile.am
index d7eb4d28..a77d5387 100644
--- a/gst/hdvparse/Makefile.am
+++ b/gst/hdvparse/Makefile.am
@@ -7,7 +7,7 @@ noinst_HEADERS = \
gsthdvparse.h
libgsthdvparse_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS)
-libgsthdvparse_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS)
+libgsthdvparse_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) $(LIBM)
libgsthdvparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgsthdvparse_la_LIBTOOLFLAGS = --tag=disable-static
diff --git a/gst/hdvparse/gsthdvparse.c b/gst/hdvparse/gsthdvparse.c
index 9914cfa2..8114f3b8 100644
--- a/gst/hdvparse/gsthdvparse.c
+++ b/gst/hdvparse/gsthdvparse.c
@@ -35,6 +35,8 @@
#include "config.h"
#endif
+#include <math.h>
+
#include <gst/gst.h>
#include <gst/base/gstbasetransform.h>
@@ -55,90 +57,25 @@ enum
PROP_0,
};
-static gchar *aperture_table[] = {
- "???",
- "cls",
- "1.0",
- "1.2",
- "1.4",
- "1.6",
- "1.7",
- "1.8",
- "2.0",
- "2.2",
- "2.4",
- "2.6",
- "2.8",
- "3.1",
- "3.4",
- "3.7",
- "4.0",
- "4.4",
- "4.8",
- "5.2",
- "5.6",
- "6.2",
- "6.8",
- "7.3",
- "8.0",
- "8.7",
- "9.6",
- "10",
- "11",
- "12",
- "14",
- "14",
- "16",
- "17",
- "18",
- "6.7"
-};
-/* Observations from my HDV Camera (Canon HV20 Pal)
- * FIXME : replace with with code once we've figured out the algorithm.
- * Shutter speed 0x4f 0x50
- * ------------------------------------
- * 1/6 F3 95
- * 1/8 90 91
- * 1/12 FA 8A
- * 1/15 C8 88
- * 1/24 7D 85
- * 1/30 64 84
- * 1/48 BE 82
- * 1/60 32 82
- * 1/100 51 81
- * 1/250 87 80
- * 1/500 43 80
- * 1/1000 22 80
- * 1/2000 11 80
- */
-typedef struct
-{
- guint vala, valb, shutter;
-} Shutter_t;
-
-static Shutter_t shutter_table[] = {
- {0xf3, 0x95, 6},
- {0x90, 0x91, 8},
- {0xfa, 0x8a, 12},
- {0xc8, 0x88, 15},
- {0x7d, 0x85, 24},
- {0x64, 0x84, 30},
- {0xbe, 0x82, 48},
- {0x32, 0x82, 60},
- {0x51, 0x81, 100},
- {0x87, 0x80, 250},
- {0x43, 0x80, 500},
- {0x22, 0x80, 1000},
- {0x11, 0x80, 2000}
-};
+
+#define CLOCK_BASE 9LL
+#define CLOCK_FREQ (CLOCK_BASE * 10000)
+
+#define MPEGTIME_TO_GSTTIME(time) (gst_util_uint64_scale ((time), \
+ GST_MSECOND/10, CLOCK_BASE))
+#define GSTTIME_TO_MPEGTIME(time) (gst_util_uint64_scale ((time), \
+ CLOCK_BASE, GST_MSECOND/10))
+
+/* If set to 1, then extra validation will be applied to check
+ * for complete spec compliance wherever applicable. */
+#define VALIDATE 0
/* Binary-coded decimal reading macro */
#define BCD(c) ( ((((c) >> 4) & 0x0f) * 10) + ((c) & 0x0f) )
/* Same as before, but with a mask */
#define BCD_M(c, mask) (BCD ((c) & (mask)))
-
/* the capabilities of the inputs and outputs.
*
* describe the real formats here.
@@ -146,13 +83,14 @@ static Shutter_t shutter_table[] = {
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("private/hdv-a1")
+ GST_STATIC_CAPS ("hdv/aux-v;hdv/aux-a")
);
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("private/hdv-a1,parsed=(boolean)True")
+ GST_STATIC_CAPS
+ ("hdv/aux-v,parsed=(boolean)True;hdv/aux-a,parsed=(boolean)True")
);
/* debug category for fltering log messages
@@ -167,6 +105,8 @@ GST_BOILERPLATE_FULL (GstHDVParse, gst_hdvparse, GstBaseTransform,
static GstFlowReturn gst_hdvparse_transform_ip (GstBaseTransform * base,
GstBuffer * outbuf);
+static GstCaps *gst_hdvparse_transform_caps (GstBaseTransform * trans,
+ GstPadDirection dir, GstCaps * incaps);
/* GObject vmethod implementations */
@@ -194,6 +134,8 @@ gst_hdvparse_class_init (GstHDVParseClass * klass)
{
GST_BASE_TRANSFORM_CLASS (klass)->transform_ip =
GST_DEBUG_FUNCPTR (gst_hdvparse_transform_ip);
+ GST_BASE_TRANSFORM_CLASS (klass)->transform_caps =
+ GST_DEBUG_FUNCPTR (gst_hdvparse_transform_caps);
}
/* initialize the new element
@@ -208,123 +150,714 @@ gst_hdvparse_init (GstHDVParse * filter, GstHDVParseClass * klass)
gst_base_transform_set_passthrough (transform, TRUE);
}
-static guint
-get_shutter_speed (guint8 vala, guint8 valb)
+static GstCaps *
+gst_hdvparse_transform_caps (GstBaseTransform * trans, GstPadDirection dir,
+ GstCaps * incaps)
{
- guint i;
+ GstCaps *res = NULL;
+ GstStructure *st = gst_caps_get_structure (incaps, 0);
- for (i = 0; i < G_N_ELEMENTS (shutter_table); i++)
- if (shutter_table[i].vala == vala && shutter_table[i].valb == valb)
- return shutter_table[i].shutter;
- GST_WARNING ("Unknown shutter speed ! vala:0x%02x, valb:0x%02x", vala, valb);
- return 0;
+ GST_WARNING_OBJECT (trans, "dir:%d, incaps:%" GST_PTR_FORMAT, dir, incaps);
+
+ if (dir == GST_PAD_SINK) {
+ res = gst_caps_new_simple (gst_structure_get_name (st),
+ "parsed", G_TYPE_BOOLEAN, TRUE, NULL);
+ } else {
+ res = gst_caps_new_simple (gst_structure_get_name (st), NULL);
+ }
+
+ return res;
}
-static void
-gst_hdvparse_parse (GstHDVParse * filter, GstBuffer * buf)
+
+static inline const gchar *
+sfr_to_framerate (guint8 sfr)
{
- guint8 *data = GST_BUFFER_DATA (buf);
- guint apertured, shutter;
- gfloat gain;
- gboolean dst = FALSE;
- GstStructure *str;
- GstMessage *msg;
-
- GST_MEMDUMP_OBJECT (filter, "BUFFER", data, GST_BUFFER_SIZE (buf));
-
- str = gst_structure_empty_new ("HDV");
-
- /* 0x1f - 0x23 : TimeCode */
-
- if (data[0x1f] != 0xff) {
- guint8 tframe, tsec, tmin, thour;
- gchar *timecode = NULL;
- tframe = BCD (data[0x1f] & 0x3f);
- tsec = BCD (data[0x20] & 0x7f);
- tmin = BCD (data[0x21] & 0x7f);
- thour = BCD (data[0x22] & 0x3f);
-
- timecode =
- g_strdup_printf ("%01d:%02d:%02d.%02d", thour, tmin, tsec, tframe);
- gst_structure_set (str, "timecode", G_TYPE_STRING, timecode, NULL);
- g_free (timecode);
- GST_LOG_OBJECT (filter, timecode);
+ switch (sfr) {
+ case 4:
+ return "30000/1001";
+ case 3:
+ return "25/1";
+ case 1:
+ return "24000/1001";
+ default:
+ return "RESERVED";
}
+}
- /* 0x23 : Timezone / Dailight Saving Time */
- /* 0x24 - 0x2a : Original time */
- if (data[0x23] != 0xff) {
- GDate *date = NULL;
- guint tzone = 0;
- guint day, month, year, hour, min, sec;
- gchar *datetime;
-
- tzone = data[0x23];
- dst = !(tzone & 0x80);
- tzone =
- BCD (tzone & 0x1f) > 12 ? BCD (tzone & 0x1f) - 12 : BCD (tzone & 0x1f);
- GST_LOG_OBJECT (filter, "TimeZone : %d, DST : %d", tzone, dst);
-
- day = BCD_M (data[0x24], 0x3f);
- month = BCD_M (data[0x25], 0x1f);
- year = BCD (data[0x26]);
- if (year > 90)
- year += 1900;
- else
- year += 2000;
- /* 0x27: ??? */
- sec = BCD_M (data[0x28], 0x7f);
- min = BCD_M (data[0x29], 0x7f);
- hour = BCD_M (data[0x2a], 0x3f);
-
- /* FIXME : we need a date/time object ! */
- date = g_date_new_dmy (day, month, year);
- datetime =
- g_strdup_printf ("%02d/%02d/%02d %02d:%02d:%02d", day, month, year,
- hour, min, sec);
- gst_structure_set (str, "date", GST_TYPE_DATE, date, "recording-time",
- G_TYPE_STRING, datetime, NULL);
- g_free (datetime);
- GST_LOG_OBJECT (filter, datetime);
+static GstFlowReturn
+parse_dv_multi_pack (GstHDVParse * filter, guint8 * data, guint64 size,
+ GstStructure * st)
+{
+ guint64 offs = 1;
+
+ while (size / 5) {
+ GST_LOG ("DV pack 0x%x", data[offs]);
+ switch (data[offs]) {
+ case 0x70:{
+ guint8 irispos, ae, agc, wbmode, whitebal, focusmode, focuspos;
+
+ irispos = data[offs + 1] & 0x3f;
+ ae = data[offs + 2] >> 4;
+ agc = data[offs + 2] & 0xf;
+ wbmode = data[offs + 3] >> 5;
+ whitebal = data[offs + 3] & 0x1f;
+ focusmode = data[offs + 4] >> 7;
+ focuspos = data[offs + 4] & 0x7f;
+
+ GST_LOG (" Consumer Camera 1");
+
+ GST_LOG (" Iris position %d (0x%x)", irispos, irispos);
+ /* Iris position = 2 ^ (IP/8) (for 0 < IP < 0x3C) */
+ if (irispos < 0x3c) {
+ GST_LOG (" IRIS F%0.2f", powf (2.0, (((float) irispos) / 8.0)));
+ gst_structure_set (st, "aperture-fnumber", G_TYPE_FLOAT,
+ powf (2.0, (((float) irispos) / 8.0)), NULL);
+ } else if (irispos == 0x3d) {
+ GST_LOG (" IRIS < 1.0");
+ } else if (irispos == 0x3e) {
+ GST_LOG (" IRIS closed");
+ }
+
+ /* AE Mode:
+ * 0 : Full automatic
+ * 1 : Gain Priority mode
+ * 2 : Shutter Priority mode
+ * 3 : Iris priority mode
+ * 4 : Manual
+ * ..: Reserved
+ * F : No information */
+ GST_LOG (" AE Mode: %d (0x%x)", ae, ae);
+
+ GST_LOG (" AGC: %d (0x%x)", agc, agc);
+ if (agc < 0xd) {
+ /* This is what the spec says.. but I'm not seeing the same on my camera :( */
+ GST_LOG (" Gain:%02.2fdB", (agc * 3.0) - 3.0);
+ gst_structure_set (st, "gain", G_TYPE_FLOAT, (agc * 3.0) - 3.0, NULL);
+ }
+ /* White balance mode
+ * 0 : Automatic
+ * 1 : hold
+ * 2 : one push
+ * 3 : pre-set
+ * 7 : no-information */
+ if (wbmode != 7)
+ GST_LOG (" White balance mode : %d (0x%x)", wbmode, wbmode);
+ /* White balance
+ * 0 : Candle
+ * 1 : Incandescent lamp
+ * 2 : low color temperature fluorescent lamp
+ * 3 : high color temperature fluorescent lamp
+ * 4 : sunlight
+ * 5 : cloudy weather
+ * F : No information
+ */
+ if (whitebal != 0xf)
+ GST_LOG (" White balance : %d (0x%x)", whitebal, whitebal);
+ if (focuspos != 0x7f) {
+ GST_LOG (" Focus mode : %s", focusmode ? "MANUAL" : "AUTOMATIC");
+ GST_LOG (" Focus position: %d (0x%x)", focuspos, focuspos);
+ }
+ }
+ break;
+ case 0x71:{
+ guint8 v_pan, h_pan, focal_length, e_zoom;
+ gboolean is, zen;
+
+ v_pan = data[offs + 1] & 0x3f;
+ is = data[offs + 2] >> 7;
+ h_pan = data[offs + 2] & 0x7f;
+ focal_length = data[offs + 3];
+ zen = data[offs + 4] >> 7;
+ e_zoom = data[offs + 4] & 0x7f;
+
+ GST_LOG (" Consumer Camera 2");
+ if (v_pan != 0x3f)
+ GST_LOG (" Vertical Panning : %d (0x%d)", v_pan, v_pan);
+ if (h_pan != 0x7f)
+ GST_LOG (" Horizontal Panning : %d (0x%d)", h_pan, h_pan);
+ GST_LOG (" Stabilizer : %s", is ? "OFF" : "ON");
+ if (focal_length != 0xff)
+ GST_LOG (" Focal Length : %f mm",
+ (focal_length & 0x7f) * pow (10, focal_length & 0x80));
+ if (zen == 0)
+ GST_LOG (" Electric Zoom %02dd.%03d", e_zoom >> 5, e_zoom & 0x1f);
+ }
+ break;
+ case 0x7f:{
+ guint16 speed;
+ guint16 speedint;
+
+ GST_LOG (" Shutter");
+ if (data[offs + 1] != 0xff)
+ GST_LOG (" Shutter Speed (1) : %d, 0x%x",
+ data[offs + 1], data[offs + 1]);
+ if (data[offs + 2] != 0xff)
+ GST_LOG (" Shutter Speed (1) : %d, 0x%x",
+ data[offs + 2], data[offs + 2]);
+
+ speed = data[offs + 3] | (data[offs + 4] & 0x7f) << 8;
+
+ /* The shutter speed is 1/(CSS * horizontal scanning period) */
+ /* FIXME : 34000 is a value interpolated by observations */
+ speedint = (int) (34000.0 / (float) speed);
+ /* Only the highest two decimal digits are valid */
+ if (speedint > 100)
+ speedint = speedint / 10 * 10;
+
+ GST_LOG (" Shutter speed : 1/%d", speedint);
+ gst_structure_set (st, "shutter-speed", GST_TYPE_FRACTION,
+ 1, speedint, NULL);
+ }
+ break;
+ default:
+ GST_MEMDUMP ("Unknown pack", data + offs, 5);
+ break;
+ }
+ size -= 5;
+ offs += 5;
}
+ return GST_FLOW_OK;
+}
- /* 0x2b : Various flags, including scene-change */
- if (!((data[0x2b] & 0x20) >> 5)) {
- GST_LOG_OBJECT (filter, "Scene change !");
- gst_structure_set (str, "scene-change", G_TYPE_BOOLEAN, TRUE, NULL);
+static GstFlowReturn
+parse_video_frame (GstHDVParse * filter, guint8 * data, guint64 size,
+ GstStructure * st)
+{
+ guint32 etn, bitrate;
+ guint8 nbframes, data_h, hdr_size, sfr, sdm;
+ guint8 aspect, framerate, profile, level, format, chroma;
+ guint8 gop_n, gop_m, cgms, recst, abst;
+ guint16 vbv_delay, width, height, vbv_buffer;
+ guint64 dts;
+ gboolean pf, tf, rf;
+
+ GST_LOG_OBJECT (filter, "Video Frame Pack");
+
+ /* Byte | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ * ---------------------------------
+ * 0 | Size (0x39) |
+ * ---------------------------------
+ * 1 | |
+ * 2 | ETN |
+ * 3 | |
+ * ---------------------------------
+ */
+
+ if (data[0] != 0x39) {
+ GST_WARNING ("Invalid size for Video frame");
+ return GST_FLOW_ERROR;
+ }
+ etn = data[3] << 16 | data[2] << 8 | data[1];
+
+ GST_LOG_OBJECT (filter, " ETN : %" G_GUINT32_FORMAT, etn);
+
+ /* Pack-V Information
+ * ---------------------------------
+ * 4 | Number of Video Frames |
+ * ---------------------------------
+ * 5 | 0 | 0 | 0 | 0 | DATA-H |
+ * ---------------------------------
+ * 6 | VBV |
+ * 7 | DELAY |
+ * ---------------------------------
+ * 8 | HEADER SIZE |
+ * ---------------------------------
+ * 9 | |
+ * 10 | DTS |
+ * 11 | |
+ * 12 | |
+ * ----------------------------- |
+ * 13 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
+ * ---------------------------------
+ * 14 |PF |TF |RF | 0 | SFR |
+ * ---------------------------------
+ */
+
+ nbframes = data[4];
+
+ if (VALIDATE && (data[5] >> 4))
+ return GST_FLOW_ERROR;
+ data_h = data[5] & 0xf;
+
+ vbv_delay = data[6] | data[7] << 8;
+
+ hdr_size = data[8];
+
+ dts = data[9] | data[10] << 8 | data[11] << 16 | data[12] << 24;
+ dts |= (guint64) (data[13] & 0x1) << 32;
+ if (G_UNLIKELY (VALIDATE && (data[13] & 0xfe))) {
+ return GST_FLOW_ERROR;
}
- /* Check for partials */
- if (GST_BUFFER_SIZE (buf) < 0x50) {
- goto beach;
+ pf = data[14] & 0x80;
+ tf = data[14] & 0x40;
+ rf = data[14] & 0x20;
+ if (G_UNLIKELY (VALIDATE && (data[14] & 0x10)))
+ return GST_FLOW_ERROR;
+
+ sfr = data[14] & 0x07;
+
+ GST_LOG_OBJECT (filter, " Pack-V Information");
+ GST_LOG_OBJECT (filter, " Number of Video Frames : %d", nbframes);
+ GST_LOG_OBJECT (filter, " Leading PES-V picture type %s (0x%x)",
+ (data_h == 0x1) ? "I-picture" : "other", data_h);
+ GST_LOG_OBJECT (filter, " VBV Delay of first frame: %" G_GUINT32_FORMAT,
+ vbv_delay);
+ GST_LOG_OBJECT (filter, " Header Size:%d", hdr_size);
+ GST_LOG_OBJECT (filter, " DTS: %" GST_TIME_FORMAT " (%" G_GUINT64_FORMAT ")",
+ GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (dts)), dts);
+ GST_LOG_OBJECT (filter, " Video source : %s %s %s (0x%x 0x%x 0x%x)",
+ pf ? "Progressive" : "Interlaced",
+ tf ? "TFF" : "", rf ? "RFF" : "", pf, tf, rf);
+ GST_LOG_OBJECT (filter, " Source Frame Rate : %s (0x%x)",
+ sfr_to_framerate (sfr), sfr);
+
+ gst_structure_set (st, "DTS", G_TYPE_UINT64, MPEGTIME_TO_GSTTIME (dts),
+ "interlaced", G_TYPE_BOOLEAN, !pf, NULL);
+
+ /* Search Data Mode
+ * ---------------------------------
+ * 15 | Search Data Mode |
+ * ---------------------------------
+ */
+ sdm = data[15];
+ GST_LOG_OBJECT (filter, " Search Data Mode : 0x%x", sdm);
+ GST_LOG_OBJECT (filter, " %s %s %s",
+ sdm & 0x2 ? "8x-Base" : "",
+ sdm & 0x4 ? "8x-Helper" : "", sdm & 0x10 ? "24x" : "");
+
+ /* Video Mode
+ * ---------------------------------
+ * 16 | Horizontal size |
+ * ----------------- |
+ * 17 | 0 | 0 | 0 | 0 | |
+ * ---------------------------------
+ * 18 | Vertical size |
+ * ----------------- |
+ * 19 | 0 | 0 | 0 | 0 | |
+ * ---------------------------------
+ * 20 | Aspect ratio | Frame Rate |
+ * ---------------------------------
+ * 21 | |
+ * 22 | bitrate |
+ * ------------------------- |
+ * 23 | 0 | 0 | 0 | 0 | 0 | 0 | |
+ * ---------------------------------
+ * 24 | VBV Buffer size |
+ * ------------------------- |
+ * 25 | 0 | 0 | 0 | 0 | 0 | 0 | |
+ * ---------------------------------
+ * 26 | 0 | Profile | Level |
+ * ---------------------------------
+ * 27 | 0 | Format |Chroma | 0 | 0 |
+ * ---------------------------------
+ * 28 | GOP N | GOP M |
+ * ---------------------------------
+ */
+ width = data[16] | (data[17] & 0xf) << 8;
+ height = data[18] | (data[19] & 0xf) << 8;
+ if (VALIDATE && ((data[17] & 0xf0) || data[19] & 0xf0))
+ return GST_FLOW_ERROR;
+ aspect = data[20] >> 4;
+ framerate = data[20] & 0xf;
+ bitrate = data[21] | data[22] << 8 | (data[23] & 0x3) << 16;
+ if (VALIDATE && (data[23] & 0xfc))
+ return GST_FLOW_ERROR;
+ vbv_buffer = data[24] | (data[25] & 0x3) << 8;
+ if (VALIDATE && (data[25] & 0xfc))
+ return GST_FLOW_ERROR;
+ profile = (data[26] >> 4) & 0x7;
+ level = data[26] & 0xf;
+ format = (data[27] >> 4) & 0x7;
+ chroma = (data[27] >> 2) & 0x3;
+ gop_n = data[28] >> 3;
+ gop_m = data[28] & 0x7;
+
+ GST_LOG_OBJECT (filter, " Video Mode");
+ GST_LOG_OBJECT (filter, " width:%d, height:%d", width, height);
+ GST_LOG_OBJECT (filter, " Aspect Ratio : %s (0x%x)",
+ (aspect == 0x3) ? "16/9" : "RESERVED", aspect);
+ GST_LOG_OBJECT (filter, " Framerate: %s (0x%x)",
+ sfr_to_framerate (framerate), framerate);
+ GST_LOG_OBJECT (filter, " Bitrate: %d bit/s", bitrate * 400);
+ GST_LOG_OBJECT (filter, " VBV buffer Size : %d bits",
+ vbv_buffer * 16 * 1024);
+ GST_LOG_OBJECT (filter, " MPEG Profile : %s (0x%x)",
+ (profile == 0x4) ? "Main" : "RESERVED", profile);
+ GST_LOG_OBJECT (filter, " MPEG Level : %s (0x%x)",
+ (level == 0x6) ? "High-1440" : "RESERVED", level);
+ GST_LOG_OBJECT (filter, " Video format : %s (0x%x)",
+ (format == 0) ? "Component" : "Reserved", format);
+ GST_LOG_OBJECT (filter, " Chroma : %s (0x%x)",
+ (chroma == 0x1) ? "4:2:0" : "RESERVED", chroma);
+ GST_LOG_OBJECT (filter, " GOP N/M : %d / %d", gop_n, gop_m);
+
+ /* data availability
+ * ---------------------------------
+ * 29 | 0 | 0 | 0 | 0 | 0 |PE2|PE1|PE0|
+ * ---------------------------------
+ * PE0 : HD2 TTC is valid
+ * PE1 : REC DATE is valid
+ * PE2 : REC TIME is valid
+ */
+ if (data[29] & 0x1) {
+ guint8 fr, sec, min, hr;
+ gboolean bf, df;
+ gchar *ttcs;
+
+ /* HD2 TTC
+ * ---------------------------------
+ * 30 |BF |DF |Tens Fr|Units of Frames|
+ * ---------------------------------
+ * 31 | 1 |Tens second|Units of Second|
+ * ---------------------------------
+ * 32 | 1 |Tens minute|Units of Minute|
+ * ---------------------------------
+ * 33 | 1 | 1 |Tens Hr|Units of Hours |
+ * ---------------------------------
+ */
+ bf = data[30] >> 7;
+ df = (data[30] >> 6) & 0x1;
+ fr = BCD (data[30] & 0x3f);
+ sec = BCD (data[31] & 0x7f);
+ min = BCD (data[32] & 0x7f);
+ hr = BCD (data[33] & 0x3f);
+ GST_LOG_OBJECT (filter, " HD2 Title Time Code");
+ GST_LOG_OBJECT (filter, " BF:%d, Drop Frame:%d", bf, df);
+ ttcs = g_strdup_printf ("%02d:%02d:%02d.%02d", hr, min, sec, fr);
+ GST_LOG_OBJECT (filter, " Timecode %s", ttcs);
+ /* FIXME : Use framerate information from above to convert to GstClockTime */
+ gst_structure_set (st, "title-time-code", G_TYPE_STRING, ttcs, NULL);
+ g_free (ttcs);
+
}
- /* 0x43 : Aperture */
- apertured = data[0x43] & 0x3f;
- if (apertured < 35) {
- GST_LOG_OBJECT (filter, "Aperture : F%s", aperture_table[apertured]);
- gst_structure_set (str, "aperture", G_TYPE_STRING,
- aperture_table[apertured], NULL);
- } else {
- GST_LOG_OBJECT (filter, "Aperture : %d", apertured);
+ if (data[29] & 0x2) {
+ gboolean ds, tm;
+ guint8 tz, day, dow, month, year;
+ GDate *date;
+
+ /* REC DATE
+ * ---------------------------------
+ * 34 |DS |TM |Tens TZ|Units of TimeZn|
+ * ---------------------------------
+ * 35 | 1 | 1 |Tens dy| Units of Days |
+ * ---------------------------------
+ * 36 | Week |TMN|Units of Months|
+ * ---------------------------------
+ * 37 | Tens of Years |Units of Years |
+ * ---------------------------------
+ */
+ ds = data[32] >> 7;
+ tm = (data[32] >> 6) & 0x1;
+ tz = BCD (data[32] & 0x3f);
+ day = BCD (data[35] & 0x3f);
+ dow = data[36] >> 5;
+ month = BCD (data[36] & 0x1f);
+ year = BCD (data[37]);
+
+ GST_LOG_OBJECT (filter, " REC DATE");
+ GST_LOG_OBJECT (filter, " ds:%d, tm:%d", ds, tm);
+ GST_LOG_OBJECT (filter, " Timezone: %d", tz);
+ GST_LOG_OBJECT (filter, " Date: %d %02d/%02d/%04d", dow, day, month, year);
+ date = g_date_new_dmy (day, month, year);
+ gst_structure_set (st, "date", GST_TYPE_DATE, date,
+ "timezone", G_TYPE_INT, tz,
+ "daylight-saving", G_TYPE_BOOLEAN, ds, NULL);
+ g_date_free (date);
+ }
+
+ if (data[29] & 0x4) {
+ guint8 fr, sec, min, hr;
+ gchar *times;
+
+ /* REC TIME
+ * ---------------------------------
+ * 38 | 1 | 1 |Tens Fr|Units of Frames|
+ * ---------------------------------
+ * 39 | 1 |Tens second|Units of Second|
+ * ---------------------------------
+ * 40 | 1 |Tens minute|Units of Minute|
+ * ---------------------------------
+ * 41 | 1 | 1 |Tens Hr|Units of Hours |
+ * ---------------------------------
+ */
+ fr = BCD (data[38] & 0x3f);
+ sec = BCD (data[39] & 0x7f);
+ min = BCD (data[40] & 0x7f);
+ hr = BCD (data[41] & 0x3f);
+ times = g_strdup_printf ("%02d:%02d:%02d", hr, min, sec);
+ GST_LOG_OBJECT (filter, " REC TIME %02d:%02d:%02d.%02d", hr, min, sec, fr);
+ gst_structure_set (st, "time", G_TYPE_STRING, times, NULL);
+ g_free (times);
+ }
+
+ /* MISC
+ * ---------------------------------
+ * 42 | CGMS |REC|ABS| 0 | 0 | 0 | 0 |
+ * ---------------------------------
+ */
+ cgms = data[42] >> 6;
+ recst = (data[42] >> 5) & 0x1;
+ abst = (data[42] >> 4) & 0x1;
+
+ GST_LOG_OBJECT (filter, " CGMS:0x%x", cgms);
+ GST_LOG_OBJECT (filter, " Recording Start Point : %s",
+ (recst == 0) ? "PRESENT" : "ABSENT");
+ GST_LOG_OBJECT (filter, " ABST : %s",
+ (abst == 0) ? "DISCONTINUITY" : "NO DISCONTINUITY");
+
+ gst_structure_set (st, "recording-start-point", G_TYPE_BOOLEAN, !recst, NULL);
+
+ /* Extended DV Pack #1
+ * 43 - 47
+ */
+ GST_LOG_OBJECT (filter, " Extended DV Pack #1 : 0x%x", data[43]);
+
+ /* Extended DV Pack #1
+ * 48 - 52
+ */
+ GST_LOG_OBJECT (filter, " Extended DV Pack #2 : 0x%x", data[48]);
+
+ /* Extended DV Pack #1
+ * 53 - 57
+ */
+ GST_LOG_OBJECT (filter, " Extended DV Pack #3 : 0x%x", data[53]);
+
+ return GST_FLOW_OK;
+
+}
+
+static GstFlowReturn
+parse_audio_frame (GstHDVParse * filter, guint8 * data, guint64 size,
+ GstStructure * st)
+{
+ guint32 etn;
+ guint8 nbmute, nbaau;
+ guint64 pts;
+ guint16 audio_comp;
+ guint8 bitrate, fs, compress, channel;
+ guint8 option, cgms;
+ gboolean acly, recst;
+
+ GST_LOG_OBJECT (filter, "Audio Frame Pack");
+
+ /* Byte | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ * ---------------------------------
+ * 0 | Size (0x0f) |
+ * ---------------------------------
+ * 1 | |
+ * 2 | ETN |
+ * 3 | |
+ * ---------------------------------
+ * 4 |Nb Audio Mute | Number of AAU |
+ * ---------------------------------
+ */
+
+ if (data[0] != 0x0f) {
+ GST_WARNING ("Invalid size for audio frame");
+ return GST_FLOW_ERROR;
+ }
+ etn = data[3] << 16 | data[2] << 8 | data[1];
+
+ GST_LOG_OBJECT (filter, " ETN : %" G_GUINT32_FORMAT, etn);
+
+ /* Pack-A Information
+ * ---------------------------------
+ * 4 |Nb Audio Mute | Number of AAU |
+ * ---------------------------------
+ * 5 | |
+ * 6 | PTS |
+ * 7 | |
+ * 8 | |
+ * ----------------------------- |
+ * 9 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
+ * ---------------------------------
+ * 10 | Audio |
+ * 11 | Compensation |
+ * ---------------------------------
+ */
+
+ /* Number of Audio Mute Frames */
+ nbmute = data[4] >> 4;
+ /* Number of AAU */
+ nbaau = data[4] & 0x0f;
+ /* PTS of the first AAU immediatly following */
+ pts = (data[5] | data[6] << 8 | data[7] << 16 | data[8] << 24);
+ pts |= (guint64) (data[9] & 0x1) << 32;
+ if (G_UNLIKELY (VALIDATE && (data[9] & 0xfe))) {
+ return GST_FLOW_ERROR;
}
- /* 0x44 : Gain */
- gain = ((data[0x44] & 0xf) - 1) * 1.5;
- GST_LOG_OBJECT (filter, "Gain : %03f db", gain);
- gst_structure_set (str, "gain", G_TYPE_FLOAT, gain, NULL);
+ /* Amount of compensation */
+ audio_comp = data[10] | data[11] << 8;
+
+ GST_LOG_OBJECT (filter, " Pack-A Information");
+ GST_LOG_OBJECT (filter, " Nb Audio Mute Frames : %d", nbmute);
+ GST_LOG_OBJECT (filter, " Nb AAU : %d", nbaau);
+ GST_LOG_OBJECT (filter,
+ " PTS : %" GST_TIME_FORMAT " (%" G_GUINT64_FORMAT ")",
+ GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (pts)), pts);
+ GST_LOG_OBJECT (filter, " Audio Compensation : %" G_GUINT32_FORMAT,
+ audio_comp);
+
+ /* Audio Mode
+ * ---------------------------------
+ * 12 | Bitrate Index | 0 |Samplerate |
+ * ---------------------------------
+ * 13 | Compression | Channels |
+ * ---------------------------------
+ * 14 | X | Anciliary Option |
+ * ---------------------------------
+ *
+ * X : Anciliary data present
+ */
+
+ bitrate = data[12] >> 4;
+ fs = data[12] & 0x7;
+ if (G_UNLIKELY (VALIDATE && (data[12] & 0x08)))
+ return GST_FLOW_ERROR;
+
+ compress = data[13] >> 4;
+ channel = data[13] & 0xf;
+ acly = data[14] & 0x80;
+ option = data[14] & 0x7f;
+
+ GST_LOG_OBJECT (filter, " Audio Mode");
+ GST_LOG_OBJECT (filter, " Bitrate : %s (0x%x)",
+ (bitrate == 0xe) ? "384kbps" : "RESERVED", bitrate);
+ GST_LOG_OBJECT (filter, " Samplerate : %s (0x%x)",
+ (fs == 0x1) ? "48 kHz" : "RESERVED", fs);
+ GST_LOG_OBJECT (filter, " Compression : %s (0x%x)",
+ (compress == 0x2) ? "MPEG-1 Layer II" : "RESERVED", compress);
+ GST_LOG_OBJECT (filter, " Channels : %s (0x%x)",
+ (channel == 0) ? "Stereo" : "RESERVED", channel);
+ GST_LOG_OBJECT (filter, " Anciliary data %s %s (0x%x)",
+ acly ? "PRESENT" : "ABSENT",
+ (option == 0xc) ? "IEC 13818-3" : "ABSENT/RESERVED", option);
+ /*
+ * ---------------------------------
+ * 15 | CGMS | R | 0 | 0 | 0 | 0 | 0 |
+ * ---------------------------------
+ *
+ * R : Recording Start Point
+ */
+
+ cgms = data[15] & 0xc0;
+ recst = data[15] & 0x20;
+
+ GST_LOG_OBJECT (filter, " Misc");
+ GST_LOG_OBJECT (filter, " CGMS : 0x%x", cgms);
+ GST_LOG_OBJECT (filter, " Recording Start Point %s",
+ (recst) ? "ABSENT" : "PRESENT");
+
+ gst_structure_set (st, "PTS", G_TYPE_UINT64, MPEGTIME_TO_GSTTIME (pts),
+ "recording-start-point", G_TYPE_BOOLEAN, !recst, NULL);
- /* 0x4f - 0x50 : Shutter */
- shutter = get_shutter_speed (data[0x4f], data[0x50]);
- GST_LOG_OBJECT (filter, "Shutter speed : 1/%d", shutter);
- if (shutter)
- gst_structure_set (str, "shutter-speed", GST_TYPE_FRACTION, 1, shutter,
- NULL);
+ return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_hdvparse_parse (GstHDVParse * filter, GstBuffer * buf)
+{
+ GstFlowReturn res = GST_FLOW_OK;
+ guint8 *data = GST_BUFFER_DATA (buf);
+ guint64 offs = 0;
+ guint64 insize = GST_BUFFER_SIZE (buf);
+ GstStructure *st;
+
+ /* Byte | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+ * ---------------------------------
+ * 0 | 0 | KEYWORD |
+ * (1) | LENGTH |
+ * ....
+ *
+ * KEYWORD :
+ * 0x00 - 0x3F : Constant length (5 bytes)
+ * 0x40 - 0x7F : Variable length (LENGTH + 1)
+ *
+ * LENGTH : if present, size of fields 1-N
+ *
+ * Known keyword values:
+ * 0x00-0x07 : AUX-V
+ * 0x08-0x3E : RESERVED
+ * 0x3F : AUX-N NO-INFO
+ * 0x40-0x43 : AUX-A
+ * 0x44-0x47 : AUX-V
+ * 0x48-0x4F : AUX-N
+ * 0x50-0x53 : AUX-SYS
+ * 0x54-0x7E : RESERVED
+ * 0x7F : AUX-N NULL PACK
+ */
+
+ st = gst_structure_empty_new ("hdv-aux");
+
+ while (res == GST_FLOW_OK && (offs < insize)) {
+ guint8 kw = data[offs] & 0x7f;
+ guint8 size;
+
+ /* Variable size packs */
+ if (kw >= 0x40) {
+ size = data[offs + 1];
+ } else
+ size = 4;
+
+ /* Size validation */
+ GST_DEBUG ("kw:0x%x, insize:%" G_GUINT64_FORMAT ", offs:%" G_GUINT64_FORMAT
+ ", size:%d", kw, insize, offs, size);
+ if (insize < offs + size) {
+ res = GST_FLOW_ERROR;
+ goto beach;
+ }
+
+ switch (kw) {
+ case 0x01:
+ GST_LOG ("BINARY GROUP");
+ offs += size + 1;
+ break;
+ case 0x07:
+ GST_LOG ("ETN pack");
+ break;
+ case 0x40:
+ GST_LOG ("Audio frame pack");
+ res = parse_audio_frame (filter, data + offs + 1, size, st);
+ offs += size + 2;
+ break;
+ case 0x3f:
+ GST_LOG ("NO INFO pack");
+ offs += size + 1;
+ break;
+ case 0x44:
+ GST_LOG ("Video frame pack");
+ res = parse_video_frame (filter, data + offs + 1, size, st);
+ offs += size + 2;
+ break;
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ GST_LOG ("DV multi-pack");
+ res = parse_dv_multi_pack (filter, data + offs + 1, size, st);
+ offs += size + 2;
+ break;
+ default:
+ GST_WARNING_OBJECT (filter, "Unknown AUX pack data of type 0x%x", kw);
+ res = GST_FLOW_ERROR;
+ }
+ }
beach:
- msg = gst_message_new_element (GST_OBJECT (filter), str);
- gst_element_post_message (GST_ELEMENT (filter), msg);
- return;
+ if (gst_structure_n_fields (st)) {
+ GstMessage *msg;
+ /* Emit the element message */
+ msg = gst_message_new_element (GST_OBJECT (filter), st);
+ gst_element_post_message (GST_ELEMENT (filter), msg);
+ } else
+ gst_structure_free (st);
+
+ return res;
+
}
/* GstBaseTransform vmethod implementations */
@@ -334,9 +867,7 @@ gst_hdvparse_transform_ip (GstBaseTransform * base, GstBuffer * outbuf)
{
GstHDVParse *filter = GST_HDVPARSE (base);
- gst_hdvparse_parse (filter, outbuf);
-
- return GST_FLOW_OK;
+ return gst_hdvparse_parse (filter, outbuf);
}
diff --git a/gst/id3tag/Makefile.am b/gst/id3tag/Makefile.am
index 9595be0f..108a227b 100644
--- a/gst/id3tag/Makefile.am
+++ b/gst/id3tag/Makefile.am
@@ -1,9 +1,9 @@
plugin_LTLIBRARIES = libgstid3tag.la
libgstid3tag_la_SOURCES = \
+ gstid3mux.c \
gsttagmux.c \
- id3tag.c \
- gstid3tag.c
+ id3tag.c
libgstid3tag_la_CFLAGS = \
$(GST_PLUGINS_BASE_CFLAGS) \
@@ -16,4 +16,4 @@ libgstid3tag_la_LIBADD = \
libgstid3tag_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstid3tag_la_LIBTOOLFLAGS = --tag=disable-static
-noinst_HEADERS = gstid3tag.h id3tag.h gsttagmux.h
+noinst_HEADERS = gstid3mux.h gsttagmux.h id3tag.h
diff --git a/gst/id3tag/gstid3tag.c b/gst/id3tag/gstid3mux.c
index 9c8072c0..b13bfb58 100644
--- a/gst/id3tag/gstid3tag.c
+++ b/gst/id3tag/gstid3mux.c
@@ -21,12 +21,12 @@
*/
/**
- * SECTION:element-id3tag
+ * SECTION:element-id3mux
* @see_also: #GstID3Demux, #GstTagSetter
*
* This element adds ID3v2 tags to the beginning of a stream, and ID3v1 tags
* to the end.
- *
+ *
* It defaults to writing ID3 version 2.3.0 tags (since those are the most
* widely supported), but can optionally write version 2.4.0 tags.
*
@@ -37,7 +37,7 @@
* <refsect2>
* <title>Example pipelines</title>
* |[
- * gst-launch -v filesrc location=foo.ogg ! decodebin ! audioconvert ! lame ! id3tag ! filesink location=foo.mp3
+ * gst-launch -v filesrc location=foo.ogg ! decodebin ! audioconvert ! lame ! id3mux ! filesink location=foo.mp3
* ]| A pipeline that transcodes a file from Ogg/Vorbis to mp3 format with
* ID3 tags that contain the same metadata as the the Ogg/Vorbis file.
* Make sure the Ogg/Vorbis file actually has comments to preserve.
@@ -51,13 +51,13 @@
#include <config.h>
#endif
-#include "gstid3tag.h"
+#include "gstid3mux.h"
#include <gst/tag/tag.h>
#include <string.h>
-GST_DEBUG_CATEGORY (gst_id3_tag_debug);
-#define GST_CAT_DEFAULT gst_id3_tag_debug
+GST_DEBUG_CATEGORY (gst_id3_mux_debug);
+#define GST_CAT_DEFAULT gst_id3_mux_debug
enum
{
@@ -67,7 +67,7 @@ enum
ARG_V2_MAJOR_VERSION
};
-#define DEFAULT_WRITE_V1 TRUE
+#define DEFAULT_WRITE_V1 FALSE
#define DEFAULT_WRITE_V2 TRUE
#define DEFAULT_V2_MAJOR_VERSION 3
@@ -76,20 +76,20 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/x-id3"));
-GST_BOILERPLATE (GstId3Tag, gst_id3_tag, GstTagMux, GST_TYPE_TAG_MUX);
+GST_BOILERPLATE (GstId3Mux, gst_id3_mux, GstTagMux, GST_TYPE_TAG_MUX);
-static GstBuffer *gst_id3_tag_render_v2_tag (GstTagMux * mux,
+static GstBuffer *gst_id3_mux_render_v2_tag (GstTagMux * mux,
GstTagList * taglist);
-static GstBuffer *gst_id3_tag_render_v1_tag (GstTagMux * mux,
+static GstBuffer *gst_id3_mux_render_v1_tag (GstTagMux * mux,
GstTagList * taglist);
-static void gst_id3_tag_set_property (GObject * object, guint prop_id,
+static void gst_id3_mux_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
-static void gst_id3_tag_get_property (GObject * object, guint prop_id,
+static void gst_id3_mux_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void
-gst_id3_tag_base_init (gpointer g_class)
+gst_id3_mux_base_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
@@ -101,18 +101,15 @@ gst_id3_tag_base_init (gpointer g_class)
"Adds an ID3v2 header and ID3v1 footer to a file",
"Michael Smith <msmith@songbirdnest.com>, "
"Tim-Philipp Müller <tim centricular net>");
-
- GST_DEBUG_CATEGORY_INIT (gst_id3_tag_debug, "id3tag", 0,
- "ID3 v1 and v2 tag muxer");
}
static void
-gst_id3_tag_class_init (GstId3TagClass * klass)
+gst_id3_mux_class_init (GstId3MuxClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
- gobject_class->set_property = gst_id3_tag_set_property;
- gobject_class->get_property = gst_id3_tag_get_property;
+ gobject_class->set_property = gst_id3_mux_set_property;
+ gobject_class->get_property = gst_id3_mux_get_property;
g_object_class_install_property (gobject_class, ARG_WRITE_V1,
g_param_spec_boolean ("write-v1", "Write id3v1 tag",
@@ -131,13 +128,13 @@ gst_id3_tag_class_init (GstId3TagClass * klass)
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
GST_TAG_MUX_CLASS (klass)->render_start_tag =
- GST_DEBUG_FUNCPTR (gst_id3_tag_render_v2_tag);
+ GST_DEBUG_FUNCPTR (gst_id3_mux_render_v2_tag);
- GST_TAG_MUX_CLASS (klass)->render_end_tag = gst_id3_tag_render_v1_tag;
+ GST_TAG_MUX_CLASS (klass)->render_end_tag = gst_id3_mux_render_v1_tag;
}
static void
-gst_id3_tag_init (GstId3Tag * id3mux, GstId3TagClass * id3mux_class)
+gst_id3_mux_init (GstId3Mux * id3mux, GstId3MuxClass * id3mux_class)
{
id3mux->write_v1 = DEFAULT_WRITE_V1;
id3mux->write_v2 = DEFAULT_WRITE_V2;
@@ -146,10 +143,10 @@ gst_id3_tag_init (GstId3Tag * id3mux, GstId3TagClass * id3mux_class)
}
static void
-gst_id3_tag_set_property (GObject * object, guint prop_id,
+gst_id3_mux_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
- GstId3Tag *mux = GST_ID3TAG (object);
+ GstId3Mux *mux = GST_ID3_MUX (object);
switch (prop_id) {
case ARG_WRITE_V1:
@@ -168,10 +165,10 @@ gst_id3_tag_set_property (GObject * object, guint prop_id,
}
static void
-gst_id3_tag_get_property (GObject * object, guint prop_id,
+gst_id3_mux_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
- GstId3Tag *mux = GST_ID3TAG (object);
+ GstId3Mux *mux = GST_ID3_MUX (object);
switch (prop_id) {
case ARG_WRITE_V1:
@@ -190,23 +187,23 @@ gst_id3_tag_get_property (GObject * object, guint prop_id,
}
static GstBuffer *
-gst_id3_tag_render_v2_tag (GstTagMux * mux, GstTagList * taglist)
+gst_id3_mux_render_v2_tag (GstTagMux * mux, GstTagList * taglist)
{
- GstId3Tag *id3mux = GST_ID3TAG (mux);
+ GstId3Mux *id3mux = GST_ID3_MUX (mux);
if (id3mux->write_v2)
- return gst_id3mux_render_v2_tag (mux, taglist, id3mux->v2_major_version);
+ return id3_mux_render_v2_tag (mux, taglist, id3mux->v2_major_version);
else
return NULL;
}
static GstBuffer *
-gst_id3_tag_render_v1_tag (GstTagMux * mux, GstTagList * taglist)
+gst_id3_mux_render_v1_tag (GstTagMux * mux, GstTagList * taglist)
{
- GstId3Tag *id3mux = GST_ID3TAG (mux);
+ GstId3Mux *id3mux = GST_ID3_MUX (mux);
if (id3mux->write_v1)
- return gst_id3mux_render_v1_tag (mux, taglist);
+ return id3_mux_render_v1_tag (mux, taglist);
else
return NULL;
}
@@ -214,7 +211,10 @@ gst_id3_tag_render_v1_tag (GstTagMux * mux, GstTagList * taglist)
static gboolean
plugin_init (GstPlugin * plugin)
{
- if (!gst_element_register (plugin, "id3tag", GST_RANK_NONE, GST_TYPE_ID3TAG))
+ GST_DEBUG_CATEGORY_INIT (gst_id3_mux_debug, "id3mux", 0,
+ "ID3 v1 and v2 tag muxer");
+
+ if (!gst_element_register (plugin, "id3mux", GST_RANK_NONE, GST_TYPE_ID3_MUX))
return FALSE;
gst_tag_register_musicbrainz_tags ();
diff --git a/gst/id3tag/gstid3tag.h b/gst/id3tag/gstid3mux.h
index a9a1ad1c..eb5aa050 100644
--- a/gst/id3tag/gstid3tag.h
+++ b/gst/id3tag/gstid3mux.h
@@ -20,18 +20,18 @@
* Boston, MA 02111-1307, USA.
*/
-#ifndef GST_ID3TAG_H
-#define GST_ID3TAG_H
+#ifndef GST_ID3_MUX_H
+#define GST_ID3_MUX_H
#include "gsttagmux.h"
#include "id3tag.h"
G_BEGIN_DECLS
-typedef struct _GstId3Tag GstId3Tag;
-typedef struct _GstId3TagClass GstId3TagClass;
+typedef struct _GstId3Mux GstId3Mux;
+typedef struct _GstId3MuxClass GstId3MuxClass;
-struct _GstId3Tag {
+struct _GstId3Mux {
GstTagMux tagmux;
gboolean write_v1;
@@ -40,24 +40,24 @@ struct _GstId3Tag {
gint v2_major_version;
};
-struct _GstId3TagClass {
+struct _GstId3MuxClass {
GstTagMuxClass tagmux_class;
};
-#define GST_TYPE_ID3TAG \
- (gst_id3_tag_get_type())
-#define GST_ID3TAG(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ID3TAG,GstId3Tag))
-#define GST_ID3TAG_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ID3TAG,GstId3TagClass))
-#define GST_IS_ID3TAG(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ID3TAG))
-#define GST_IS_ID3TAG_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ID3TAG))
+#define GST_TYPE_ID3_MUX \
+ (gst_id3_mux_get_type())
+#define GST_ID3_MUX(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ID3_MUX,GstId3Mux))
+#define GST_ID3_MUX_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ID3_MUX,GstId3MuxClass))
+#define GST_IS_ID3_MUX(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ID3_MUX))
+#define GST_IS_ID3_MUX_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ID3_MUX))
-GType gst_id3_tag_get_type (void);
+GType gst_id3_mux_get_type (void);
G_END_DECLS
-#endif /* GST_ID3TAG_H */
+#endif /* GST_ID3_MUX_H */
diff --git a/gst/id3tag/gsttagmux.c b/gst/id3tag/gsttagmux.c
index 3b7ff119..4aafb96d 100644
--- a/gst/id3tag/gsttagmux.c
+++ b/gst/id3tag/gsttagmux.c
@@ -57,10 +57,10 @@ gst_tag_mux_iface_init (GType tag_type)
/* make sure to register a less generic type so we can easily move this
* GstTagMux base class into -base without causing GType name conflicts */
-typedef GstTagMux GstId3TagMux;
-typedef GstTagMuxClass GstId3TagMuxClass;
+typedef GstTagMux GstId3BaseMux;
+typedef GstTagMuxClass GstId3BaseMuxClass;
-GST_BOILERPLATE_FULL (GstId3TagMux, gst_tag_mux,
+GST_BOILERPLATE_FULL (GstId3BaseMux, gst_tag_mux,
GstElement, GST_TYPE_ELEMENT, gst_tag_mux_iface_init);
@@ -100,8 +100,8 @@ gst_tag_mux_base_init (gpointer g_class)
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_tag_mux_sink_template));
- GST_DEBUG_CATEGORY_INIT (gst_tag_mux_debug, "tagmux", 0,
- "tag muxer base class");
+ GST_DEBUG_CATEGORY_INIT (gst_tag_mux_debug, "id3basemux", 0,
+ "tag muxer base class for Id3Mux");
}
static void
diff --git a/gst/id3tag/id3tag.c b/gst/id3tag/id3tag.c
index a39e2a8e..dd7bb04f 100644
--- a/gst/id3tag/id3tag.c
+++ b/gst/id3tag/id3tag.c
@@ -25,8 +25,8 @@
#include <gst/tag/tag.h>
-GST_DEBUG_CATEGORY_EXTERN (gst_id3_tag_debug);
-#define GST_CAT_DEFAULT gst_id3_tag_debug
+GST_DEBUG_CATEGORY_EXTERN (gst_id3_mux_debug);
+#define GST_CAT_DEFAULT gst_id3_mux_debug
#define ID3V2_APIC_PICTURE_OTHER 0
#define ID3V2_APIC_PICTURE_FILE_ICON 1
@@ -1024,7 +1024,7 @@ foreach_add_tag (const GstTagList * list, const gchar * tag, gpointer userdata)
}
GstBuffer *
-gst_id3mux_render_v2_tag (GstTagMux * mux, GstTagList * taglist, int version)
+id3_mux_render_v2_tag (GstTagMux * mux, GstTagList * taglist, int version)
{
GstId3v2Tag tag;
GstBuffer *buf;
@@ -1062,17 +1062,17 @@ gst_id3mux_render_v2_tag (GstTagMux * mux, GstTagList * taglist, int version)
#define ID3_V1_TAG_SIZE 128
typedef void (*GstId3v1WriteFunc) (const GstTagList * list,
- const gchar * gst_tag, guint8 * dst, int len);
+ const gchar * gst_tag, guint8 * dst, int len, gboolean * wrote_tag);
static void
latin1_convert (const GstTagList * list, const gchar * tag,
- guint8 * dst, int maxlen)
+ guint8 * dst, int maxlen, gboolean * wrote_tag)
{
gchar *str;
gsize len;
gchar *latin1;
- if (!gst_tag_list_get_string (list, tag, &str))
+ if (!gst_tag_list_get_string (list, tag, &str) || str == NULL)
return;
/* Convert to Latin-1 (ISO-8859-1), replacing unrepresentable characters
@@ -1080,9 +1080,10 @@ latin1_convert (const GstTagList * list, const gchar * tag,
latin1 = g_convert_with_fallback (str, -1, "ISO-8859-1", "UTF-8", "?",
NULL, &len, NULL);
- if (latin1) {
+ if (latin1 != NULL && *latin1 != '\0') {
len = MIN (len, maxlen);
memcpy (dst, latin1, len);
+ *wrote_tag = TRUE;
g_free (latin1);
}
@@ -1091,7 +1092,7 @@ latin1_convert (const GstTagList * list, const gchar * tag,
static void
date_v1_convert (const GstTagList * list, const gchar * tag,
- guint8 * dst, int maxlen)
+ guint8 * dst, int maxlen, gboolean * wrote_tag)
{
GDate *date;
@@ -1102,6 +1103,7 @@ date_v1_convert (const GstTagList * list, const gchar * tag,
if (year > 500 && year < 2100) {
gchar str[5];
g_snprintf (str, 5, "%.4u", year);
+ *wrote_tag = TRUE;
memcpy (dst, str, 4);
} else {
GST_WARNING ("invalid year %u, skipping", year);
@@ -1113,14 +1115,14 @@ date_v1_convert (const GstTagList * list, const gchar * tag,
static void
genre_v1_convert (const GstTagList * list, const gchar * tag,
- guint8 * dst, int maxlen)
+ guint8 * dst, int maxlen, gboolean * wrote_tag)
{
gchar *str;
int genreidx = -1;
guint i, max;
/* We only support one genre */
- if (!gst_tag_list_get_string_index (list, tag, 0, &str))
+ if (!gst_tag_list_get_string_index (list, tag, 0, &str) || str == NULL)
return;
max = gst_tag_id3_genre_count ();
@@ -1133,15 +1135,17 @@ genre_v1_convert (const GstTagList * list, const gchar * tag,
}
}
- if (genreidx >= 0 && genreidx <= 127)
+ if (genreidx >= 0 && genreidx <= 127) {
*dst = (guint8) genreidx;
+ *wrote_tag = TRUE;
+ }
g_free (str);
}
static void
track_number_convert (const GstTagList * list, const gchar * tag,
- guint8 * dst, int maxlen)
+ guint8 * dst, int maxlen, gboolean * wrote_tag)
{
guint tracknum;
@@ -1149,8 +1153,10 @@ track_number_convert (const GstTagList * list, const gchar * tag,
if (!gst_tag_list_get_uint_index (list, tag, 0, &tracknum))
return;
- if (tracknum <= 127)
+ if (tracknum <= 127) {
*dst = (guint8) tracknum;
+ *wrote_tag = TRUE;
+ }
}
static const struct
@@ -1172,10 +1178,11 @@ static const struct
};
GstBuffer *
-gst_id3mux_render_v1_tag (GstTagMux * mux, GstTagList * taglist)
+id3_mux_render_v1_tag (GstTagMux * mux, GstTagList * taglist)
{
GstBuffer *buf = gst_buffer_new_and_alloc (ID3_V1_TAG_SIZE);
guint8 *data = GST_BUFFER_DATA (buf);
+ gboolean wrote_tag = FALSE;
int i;
memset (data, 0, ID3_V1_TAG_SIZE);
@@ -1184,9 +1191,18 @@ gst_id3mux_render_v1_tag (GstTagMux * mux, GstTagList * taglist)
data[1] = 'A';
data[2] = 'G';
+ /* Genre #0 stands for 'Blues', so init genre field to an invalid number */
+ data[127] = 255;
+
for (i = 0; i < G_N_ELEMENTS (v1_funcs); i++) {
v1_funcs[i].func (taglist, v1_funcs[i].gst_tag, data + v1_funcs[i].offset,
- v1_funcs[i].length);
+ v1_funcs[i].length, &wrote_tag);
+ }
+
+ if (!wrote_tag) {
+ GST_WARNING_OBJECT (mux, "no ID3v1 tag written (no suitable tags found)");
+ gst_buffer_unref (buf);
+ return NULL;
}
gst_buffer_set_caps (buf, GST_PAD_CAPS (mux->srcpad));
diff --git a/gst/id3tag/id3tag.h b/gst/id3tag/id3tag.h
index 1fb59376..d5504969 100644
--- a/gst/id3tag/id3tag.h
+++ b/gst/id3tag/id3tag.h
@@ -24,9 +24,9 @@ G_BEGIN_DECLS
#define ID3_VERSION_2_3 3
#define ID3_VERSION_2_4 4
-GstBuffer * gst_id3mux_render_v2_tag (GstTagMux * mux, GstTagList * taglist,
+GstBuffer * id3_mux_render_v2_tag (GstTagMux * mux, GstTagList * taglist,
int version);
-GstBuffer * gst_id3mux_render_v1_tag (GstTagMux * mux, GstTagList * taglist);
+GstBuffer * id3_mux_render_v1_tag (GstTagMux * mux, GstTagList * taglist);
G_END_DECLS
diff --git a/gst/liveadder/liveadder.c b/gst/liveadder/liveadder.c
index d10f54f0..25fa4063 100644
--- a/gst/liveadder/liveadder.c
+++ b/gst/liveadder/liveadder.c
@@ -1349,9 +1349,8 @@ no_clock:
pause:
{
- const gchar *reason = gst_flow_get_name (result);
-
- GST_DEBUG_OBJECT (adder, "pausing task, reason %s", reason);
+ GST_DEBUG_OBJECT (adder, "pausing task, reason %s",
+ gst_flow_get_name (result));
GST_OBJECT_LOCK (adder);
diff --git a/gst/mpegdemux/gstmpegdefs.h b/gst/mpegdemux/gstmpegdefs.h
index 7ad1e25c..375f1dc0 100644
--- a/gst/mpegdemux/gstmpegdefs.h
+++ b/gst/mpegdemux/gstmpegdefs.h
@@ -170,12 +170,17 @@
#define ST_PS_AUDIO_AC3 0x81
#define ST_PS_AUDIO_DTS 0x8a
#define ST_PS_AUDIO_LPCM 0x8b
-#define ST_HDV_PRIVATE_A0 0xa0
-#define ST_HDV_PRIVATE_A1 0xa1
#define ST_PS_DVD_SUBPICTURE 0xff
/* Blu-ray PGS subpictures */
#define ST_BD_PGS_SUBPICTURE 0x90
+/* HDV AUX stream mapping
+ * 0xA0 ISO/IEC 61834-11
+ * 0xA1 ISO/IEC 61834-11
+ */
+#define ST_HDV_AUX_A 0xa0
+#define ST_HDV_AUX_V 0xa1
+
/* Un-official time-code stream */
#define ST_PS_TIMECODE 0xd2
diff --git a/gst/mpegdemux/gstmpegdesc.c b/gst/mpegdemux/gstmpegdesc.c
index 4c88e1fd..0a985897 100644
--- a/gst/mpegdemux/gstmpegdesc.c
+++ b/gst/mpegdemux/gstmpegdesc.c
@@ -65,7 +65,9 @@ gst_mpeg_descriptor_parse_1 (guint8 * data, guint size)
if (length > size)
return 0;
- return length + 2;;
+ GST_MEMDUMP ("tag contents:", data, length);
+
+ return length + 2;
}
GstMPEGDescriptor *
diff --git a/gst/mpegdemux/gstmpegtsdemux.c b/gst/mpegdemux/gstmpegtsdemux.c
index b75bfc6d..cb082a2b 100644
--- a/gst/mpegdemux/gstmpegtsdemux.c
+++ b/gst/mpegdemux/gstmpegtsdemux.c
@@ -634,15 +634,15 @@ gst_mpegts_demux_fill_stream (GstMpegTSStream * stream, guint8 id,
caps = gst_caps_new_simple ("private/teletext", NULL);
}
break;
- case ST_HDV_PRIVATE_A0:
+ case ST_HDV_AUX_V:
template = klass->private_template;
name = g_strdup_printf ("private_%04x", stream->PID);
- caps = gst_caps_new_simple ("private/hdv-a0", NULL);
+ caps = gst_caps_new_simple ("hdv/aux-v", NULL);
break;
- case ST_HDV_PRIVATE_A1:
+ case ST_HDV_AUX_A:
template = klass->private_template;
name = g_strdup_printf ("private_%04x", stream->PID);
- caps = gst_caps_new_simple ("private/hdv-a1", NULL);
+ caps = gst_caps_new_simple ("hdv/aux-a", NULL);
break;
case ST_PRIVATE_SECTIONS:
case ST_MHEG:
@@ -923,8 +923,8 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first,
* to drop. */
if (stream->PMT_pid <= MPEGTS_MAX_PID && demux->streams[stream->PMT_pid]
&& demux->streams[demux->streams[stream->PMT_pid]->PMT.PCR_PID]
- && demux->streams[demux->streams[stream->PMT_pid]->PMT.
- PCR_PID]->discont_PCR) {
+ && demux->streams[demux->streams[stream->PMT_pid]->PMT.PCR_PID]->
+ discont_PCR) {
GST_WARNING_OBJECT (demux, "middle of discont, dropping");
goto bad_timestamp;
}
@@ -946,8 +946,8 @@ gst_mpegts_demux_data_cb (GstPESFilter * filter, gboolean first,
*/
if (stream->PMT_pid <= MPEGTS_MAX_PID && demux->streams[stream->PMT_pid]
&& demux->streams[demux->streams[stream->PMT_pid]->PMT.PCR_PID]
- && demux->streams[demux->streams[stream->PMT_pid]->PMT.
- PCR_PID]->last_PCR > 0) {
+ && demux->streams[demux->streams[stream->PMT_pid]->PMT.PCR_PID]->
+ last_PCR > 0) {
GST_DEBUG_OBJECT (demux, "timestamps wrapped before noticed in PCR");
time = MPEGTIME_TO_GSTTIME (pts) + stream->base_time +
MPEGTIME_TO_GSTTIME ((guint64) (1) << 33);
@@ -2884,7 +2884,8 @@ gst_mpegts_demux_chain (GstPad * pad, GstBuffer * buffer)
/* process all packets */
for (i = 0; i < sync_count; i++) {
ret = gst_mpegts_demux_parse_transport_packet (demux, demux->sync_lut[i]);
- if (G_UNLIKELY (ret == GST_FLOW_LOST_SYNC)) {
+ if (G_UNLIKELY (ret == GST_FLOW_LOST_SYNC
+ || ret == GST_FLOW_NEED_MORE_DATA)) {
ret = GST_FLOW_OK;
continue;
}
diff --git a/gst/mpegtsmux/mpegtsmux.c b/gst/mpegtsmux/mpegtsmux.c
index f24c4a3e..edda2600 100644
--- a/gst/mpegtsmux/mpegtsmux.c
+++ b/gst/mpegtsmux/mpegtsmux.c
@@ -523,6 +523,8 @@ mpegtsmux_choose_best_stream (MpegTsMux * mux)
return best;
}
+#define COLLECT_DATA_PAD(collect_data) (((GstCollectData *)(collect_data))->pad)
+
static GstFlowReturn
mpegtsmux_collected (GstCollectPads * pads, MpegTsMux * mux)
{
@@ -540,10 +542,8 @@ mpegtsmux_collected (GstCollectPads * pads, MpegTsMux * mux)
if (mux->pcr_stream == NULL) {
if (best) {
- GstCollectData *c_data = (GstCollectData *) best;
/* Take the first data stream for the PCR */
- GST_DEBUG_OBJECT (mux, "Use stream from pad %" GST_PTR_FORMAT " as PCR",
- c_data->pad);
+ GST_DEBUG_OBJECT (COLLECT_DATA_PAD (best), "Use stream as PCR");
mux->pcr_stream = best->stream;
}
}
@@ -564,14 +564,12 @@ mpegtsmux_collected (GstCollectPads * pads, MpegTsMux * mux)
if (best != NULL) {
GstBuffer *buf = best->queued_buf;
- GstCollectData *c_data = (GstCollectData *) best;
gint64 pts = -1;
g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
- GST_DEBUG_OBJECT (mux,
- "Chose stream from pad %" GST_PTR_FORMAT " for output (PID: 0x%04x)",
- c_data->pad, best->pid);
+ GST_DEBUG_OBJECT (COLLECT_DATA_PAD (best),
+ "Chose stream for output (PID: 0x%04x)", best->pid);
if (GST_CLOCK_TIME_IS_VALID (best->cur_ts)) {
pts = GSTTIME_TO_MPEGTIME (best->cur_ts);
diff --git a/gst/mpegvideoparse/mpegvideoparse.c b/gst/mpegvideoparse/mpegvideoparse.c
index 2a7f9153..d0badbfc 100644
--- a/gst/mpegvideoparse/mpegvideoparse.c
+++ b/gst/mpegvideoparse/mpegvideoparse.c
@@ -278,6 +278,64 @@ mpegvideoparse_handle_sequence (MpegVideoParse * mpegvideoparse,
return TRUE;
}
+#ifndef GST_DISABLE_GST_DEBUG
+static const gchar *
+picture_start_code_name (guint8 psc)
+{
+ guint i;
+ const struct
+ {
+ guint8 psc;
+ const gchar *name;
+ } psc_names[] = {
+ {
+ 0x00, "Picture Start"}, {
+ 0xb0, "Reserved"}, {
+ 0xb1, "Reserved"}, {
+ 0xb2, "User Data Start"}, {
+ 0xb3, "Sequence Header Start"}, {
+ 0xb4, "Sequence Error"}, {
+ 0xb5, "Extnsion Start"}, {
+ 0xb6, "Reserved"}, {
+ 0xb7, "Sequence End"}, {
+ 0xb8, "Group Start"}, {
+ 0xb9, "Program End"}
+ };
+ if (psc < 0xB0 && psc > 0)
+ return "Slice Start";
+
+ for (i = 0; i < G_N_ELEMENTS (psc_names); i++)
+ if (psc_names[i].psc == psc)
+ return psc_names[i].name;
+
+ return "UNKNOWN";
+};
+
+static const gchar *
+picture_type_name (guint8 pct)
+{
+ guint i;
+ const struct
+ {
+ guint8 pct;
+ const gchar *name;
+ } pct_names[] = {
+ {
+ 0, "Forbidden"}, {
+ 1, "I Frame"}, {
+ 2, "P Frame"}, {
+ 3, "B Frame"}, {
+ 4, "DC Intra Coded (Shall Not Be Used!)"}
+ };
+
+ for (i = 0; i < G_N_ELEMENTS (pct_names); i++)
+ if (pct_names[i].pct == pct)
+ return pct_names[i].name;
+
+ return "Reserved/Unknown";
+}
+#endif /* GST_DISABLE_GST_DEBUG */
+
static gboolean
mpegvideoparse_handle_picture (MpegVideoParse * mpegvideoparse, GstBuffer * buf)
{
@@ -289,6 +347,9 @@ mpegvideoparse_handle_picture (MpegVideoParse * mpegvideoparse, GstBuffer * buf)
cur = mpeg_util_find_start_code (&sync_word, cur, end);
while (cur != NULL) {
+ if (cur[0] == 0 || cur[0] > 0xaf)
+ GST_LOG_OBJECT (mpegvideoparse, "Picture Start Code : %s",
+ picture_start_code_name (cur[0]));
/* Cur points at the last byte of the start code */
if (cur[0] == MPEG_PACKET_PICTURE) {
guint8 *pic_data = cur - 3;
@@ -301,7 +362,8 @@ mpegvideoparse_handle_picture (MpegVideoParse * mpegvideoparse, GstBuffer * buf)
if (hdr.pic_type != MPEG_PICTURE_TYPE_I)
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
- GST_LOG_OBJECT (mpegvideoparse, "Picture type is %u", hdr.pic_type);
+ GST_LOG_OBJECT (mpegvideoparse, "Picture type is %s",
+ picture_type_name (hdr.pic_type));
/* FIXME: Can use the picture type and number of fields to track a
* timestamp */
}
@@ -359,8 +421,9 @@ mpegvideoparse_drain_avail (MpegVideoParse * mpegvideoparse)
while ((cur != NULL) && (res == GST_FLOW_OK)) {
/* Handle the block */
GST_LOG_OBJECT (mpegvideoparse,
- "Have block of size %u with pack_type 0x%02x and flags 0x%02x",
- cur->length, cur->first_pack_type, cur->flags);
+ "Have block of size %u with pack_type %s and flags 0x%02x",
+ cur->length, picture_start_code_name (cur->first_pack_type),
+ cur->flags);
/* Don't start pushing out buffers until we've seen a sequence header */
if (mpegvideoparse->seq_hdr.mpeg_version == 0) {
diff --git a/gst/mxf/mxfdemux.c b/gst/mxf/mxfdemux.c
index c33650b5..5abd37ff 100644
--- a/gst/mxf/mxfdemux.c
+++ b/gst/mxf/mxfdemux.c
@@ -439,8 +439,8 @@ gst_mxf_demux_handle_partition_pack (GstMXFDemux * demux, const MXFUL * key,
for (l = demux->partitions; l; l = l->next) {
GstMXFDemuxPartition *a, *b;
- if (l->next == NULL);
- break;
+ if (l->next == NULL)
+ break;
a = l->data;
b = l->next->data;
@@ -1921,8 +1921,8 @@ gst_mxf_demux_handle_random_index_pack (GstMXFDemux * demux, const MXFUL * key,
for (l = demux->partitions; l; l = l->next) {
GstMXFDemuxPartition *a, *b;
- if (l->next == NULL);
- break;
+ if (l->next == NULL)
+ break;
a = l->data;
b = l->next->data;
diff --git a/gst/nsf/nes6502.c b/gst/nsf/nes6502.c
index e2ed01f7..450350fc 100644
--- a/gst/nsf/nes6502.c
+++ b/gst/nsf/nes6502.c
@@ -944,7 +944,7 @@
ADD_CYCLES(cycles); \
}
-#define SEC() \
+#define SEC_6502() \
{ \
SET_FLAG(C_FLAG); \
ADD_CYCLES(2); \
@@ -1798,7 +1798,7 @@ nes6502_execute (int remaining_cycles)
break;
case 0x38: /* SEC */
- SEC ();
+ SEC_6502 ();
break;
case 0x39: /* AND $nnnn,Y */
diff --git a/gst/qtmux/atoms.c b/gst/qtmux/atoms.c
index 47db40ce..29859d00 100644
--- a/gst/qtmux/atoms.c
+++ b/gst/qtmux/atoms.c
@@ -1036,7 +1036,8 @@ atom_meta_free (AtomMETA * meta)
{
atom_full_clear (&meta->header);
atom_hdlr_clear (&meta->hdlr);
- atom_ilst_free (meta->ilst);
+ if (meta->ilst)
+ atom_ilst_free (meta->ilst);
meta->ilst = NULL;
g_free (meta);
}
@@ -1061,8 +1062,11 @@ static void
atom_udta_free (AtomUDTA * udta)
{
atom_clear (&udta->header);
- atom_meta_free (udta->meta);
+ if (udta->meta)
+ atom_meta_free (udta->meta);
udta->meta = NULL;
+ if (udta->entries)
+ atom_info_list_free (udta->entries);
g_free (udta);
}
@@ -1149,6 +1153,7 @@ atom_moov_init (AtomMOOV * moov, AtomsContext * context)
atom_mvhd_init (&(moov->mvhd));
moov->udta = NULL;
moov->traks = NULL;
+ moov->context = *context;
}
AtomMOOV *
@@ -1170,13 +1175,11 @@ atom_moov_free (AtomMOOV * moov)
walker = moov->traks;
while (walker) {
- GList *aux = walker;
-
+ atom_trak_free ((AtomTRAK *) walker->data);
walker = g_list_next (walker);
- moov->traks = g_list_remove_link (moov->traks, aux);
- atom_trak_free ((AtomTRAK *) aux->data);
- g_list_free (aux);
}
+ g_list_free (moov->traks);
+ moov->traks = NULL;
if (moov->udta) {
atom_udta_free (moov->udta);
@@ -1240,9 +1243,6 @@ atom_copy_data (Atom * atom, guint8 ** buffer, guint64 * size, guint64 * offset)
* would be a problem for size (re)write code, not to mention memory */
g_return_val_if_fail (atom->type == FOURCC_mdat, 0);
prop_copy_uint64 (atom->extended_size, buffer, size, offset);
- } else {
- /* just in case some trivially derived atom does not do so */
- atom_write_size (buffer, size, offset, original_offset);
}
return *offset - original_offset;
@@ -2163,6 +2163,10 @@ atom_udta_copy_data (AtomUDTA * udta, guint8 ** buffer, guint64 * size,
if (!atom_meta_copy_data (udta->meta, buffer, size, offset)) {
return 0;
}
+ } else if (udta->entries) {
+ /* extra atoms */
+ if (!atom_info_list_copy_data (udta->entries, buffer, size, offset))
+ return 0;
}
atom_write_size (buffer, size, offset, original_offset);
@@ -2238,6 +2242,10 @@ stsc_entry_new (guint32 first_chunk, guint32 samples, guint32 desc_index)
static void
atom_stsc_add_new_entry (AtomSTSC * stsc, guint32 first_chunk, guint32 nsamples)
{
+ if (stsc->entries &&
+ ((STSCEntry *) stsc->entries->data)->samples_per_chunk == nsamples)
+ return;
+
stsc->entries =
g_list_prepend (stsc->entries, stsc_entry_new (first_chunk, nsamples, 1));
stsc->n_entries++;
@@ -2517,16 +2525,18 @@ atom_moov_chunks_add_offset (AtomMOOV * moov, guint32 offset)
* Meta tags functions
*/
static void
-atom_moov_init_metatags (AtomMOOV * moov)
+atom_moov_init_metatags (AtomMOOV * moov, AtomsContext * context)
{
if (!moov->udta) {
moov->udta = atom_udta_new ();
}
- if (!moov->udta->meta) {
- moov->udta->meta = atom_meta_new ();
- }
- if (!moov->udta->meta->ilst) {
- moov->udta->meta->ilst = atom_ilst_new ();
+ if (context->flavor != ATOMS_TREE_FLAVOR_3GP) {
+ if (!moov->udta->meta) {
+ moov->udta->meta = atom_meta_new ();
+ }
+ if (!moov->udta->meta->ilst) {
+ moov->udta->meta->ilst = atom_ilst_new ();
+ }
}
}
@@ -2543,11 +2553,14 @@ atom_tag_data_alloc_data (AtomTagData * data, guint size)
static void
atom_moov_append_tag (AtomMOOV * moov, AtomInfo * tag)
{
- AtomILST *ilst;
+ GList **entries;
- atom_moov_init_metatags (moov);
- ilst = moov->udta->meta->ilst;
- ilst->entries = g_list_append (ilst->entries, tag);
+ atom_moov_init_metatags (moov, &moov->context);
+ if (moov->udta->meta)
+ entries = &moov->udta->meta->ilst->entries;
+ else
+ entries = &moov->udta->entries;
+ *entries = g_list_append (*entries, tag);
}
void
@@ -2621,6 +2634,87 @@ atom_moov_add_blob_tag (AtomMOOV * moov, guint8 * data, guint size)
atom_data_free));
}
+void
+atom_moov_add_3gp_tag (AtomMOOV * moov, guint32 fourcc, guint8 * data,
+ guint size)
+{
+ AtomData *data_atom;
+ GstBuffer *buf;
+ guint8 *bdata;
+
+ /* need full atom */
+ buf = gst_buffer_new_and_alloc (size + 4);
+ bdata = GST_BUFFER_DATA (buf);
+ /* full atom: version and flags */
+ GST_WRITE_UINT32_BE (bdata, 0);
+ memcpy (bdata + 4, data, size);
+
+ data_atom = atom_data_new_from_gst_buffer (fourcc, buf);
+ gst_buffer_unref (buf);
+
+ atom_moov_append_tag (moov,
+ build_atom_info_wrapper ((Atom *) data_atom, atom_data_copy_data,
+ atom_data_free));
+}
+
+guint16
+language_code (const char *lang)
+{
+ g_return_val_if_fail (lang != NULL, 0);
+ g_return_val_if_fail (strlen (lang) == 3, 0);
+
+ return (((lang[0] - 0x60) & 0x1F) << 10) + (((lang[1] - 0x60) & 0x1F) << 5) +
+ ((lang[2] - 0x60) & 0x1F);
+}
+
+void
+atom_moov_add_3gp_str_int_tag (AtomMOOV * moov, guint32 fourcc,
+ const gchar * value, gint16 ivalue)
+{
+ gint len = 0, size = 0;
+ guint8 *data;
+
+ if (value) {
+ len = strlen (value);
+ size = len + 3;
+ }
+
+ if (ivalue >= 0)
+ size += 2;
+
+ data = g_malloc (size + 3);
+ /* language tag and null-terminated UTF-8 string */
+ if (value) {
+ GST_WRITE_UINT16_BE (data, language_code (GST_QT_MUX_DEFAULT_TAG_LANGUAGE));
+ /* include 0 terminator */
+ memcpy (data + 2, value, len + 1);
+ }
+ /* 16-bit unsigned int if standalone, otherwise 8-bit */
+ if (ivalue >= 0) {
+ if (size == 2)
+ GST_WRITE_UINT16_BE (data + size - 2, ivalue);
+ else {
+ GST_WRITE_UINT8 (data + size - 2, ivalue & 0xFF);
+ size--;
+ }
+ }
+
+ atom_moov_add_3gp_tag (moov, fourcc, data, size);
+ g_free (data);
+}
+
+void
+atom_moov_add_3gp_str_tag (AtomMOOV * moov, guint32 fourcc, const gchar * value)
+{
+ atom_moov_add_3gp_str_int_tag (moov, fourcc, value, -1);
+}
+
+void
+atom_moov_add_3gp_uint_tag (AtomMOOV * moov, guint32 fourcc, guint16 value)
+{
+ atom_moov_add_3gp_str_int_tag (moov, fourcc, NULL, value);
+}
+
/*
* Functions for specifying media types
*/
@@ -2804,14 +2898,56 @@ atom_trak_set_audio_type (AtomTRAK * trak, AtomsContext * context,
atom_trak_set_constant_size_samples (trak, sample_size);
}
+AtomInfo *
+build_pasp_extension (AtomTRAK * trak, gint par_width, gint par_height)
+{
+ AtomData *atom_data;
+ GstBuffer *buf;
+ guint8 *data;
+
+ buf = gst_buffer_new_and_alloc (8);
+ data = GST_BUFFER_DATA (buf);
+
+ /* ihdr = image header box */
+ GST_WRITE_UINT32_BE (data, par_width);
+ GST_WRITE_UINT32_BE (data + 4, par_height);
+
+ atom_data = atom_data_new_from_gst_buffer (FOURCC_pasp, buf);
+ gst_buffer_unref (buf);
+
+ return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data,
+ atom_data_free);
+}
+
void
atom_trak_set_video_type (AtomTRAK * trak, AtomsContext * context,
VisualSampleEntry * entry, guint32 scale, AtomInfo * ext)
{
SampleTableEntryMP4V *ste;
+ gint dwidth, dheight;
+ gint par_n = 0, par_d = 0;
+
+ if ((entry->par_n != 1 || entry->par_d != 1) &&
+ (entry->par_n != entry->par_d)) {
+ par_n = entry->par_n;
+ par_d = entry->par_d;
+ }
+
+ dwidth = entry->width;
+ dheight = entry->height;
+ /* ISO file spec says track header w/h indicates track's visual presentation
+ * (so this together with pixels w/h implicitly defines PAR) */
+ if (par_n && (context->flavor != ATOMS_TREE_FLAVOR_MOV)) {
+ if (par_n > par_d) {
+ dwidth = entry->width * par_n / par_d;
+ dheight = entry->height;
+ } else {
+ dwidth = entry->width * par_n / par_d;
+ dheight = entry->height;
+ }
+ }
- atom_trak_set_video_commons (trak, context, scale, entry->width,
- entry->height);
+ atom_trak_set_video_commons (trak, context, scale, dwidth, dheight);
ste = atom_trak_add_video_entry (trak, context, entry->fourcc);
trak->is_video = TRUE;
@@ -2825,6 +2961,12 @@ atom_trak_set_video_type (AtomTRAK * trak, AtomsContext * context,
if (ext)
ste->extension_atoms = g_list_prepend (ste->extension_atoms, ext);
+
+ /* QT spec has a pasp extension atom in stsd that can hold PAR */
+ if (par_n && (context->flavor == ATOMS_TREE_FLAVOR_MOV)) {
+ ste->extension_atoms = g_list_append (ste->extension_atoms,
+ build_pasp_extension (trak, par_n, par_d));
+ }
}
/* some sample description construction helpers */
diff --git a/gst/qtmux/atoms.h b/gst/qtmux/atoms.h
index 23bc19bb..4c94141b 100644
--- a/gst/qtmux/atoms.h
+++ b/gst/qtmux/atoms.h
@@ -55,7 +55,8 @@
typedef enum _AtomsTreeFlavor
{
ATOMS_TREE_FLAVOR_MOV,
- ATOMS_TREE_FLAVOR_ISOM
+ ATOMS_TREE_FLAVOR_ISOM,
+ ATOMS_TREE_FLAVOR_3GP
} AtomsTreeFlavor;
typedef struct _AtomsContext
@@ -509,6 +510,9 @@ typedef struct _AtomUDTA
{
Atom header;
+ /* list of AtomInfo */
+ GList* entries;
+ /* or list is further down */
AtomMETA *meta;
} AtomUDTA;
@@ -526,6 +530,9 @@ typedef struct _AtomTRAK
typedef struct _AtomMOOV
{
+ /* style */
+ AtomsContext context;
+
Atom header;
AtomMVHD mvhd;
@@ -602,6 +609,8 @@ typedef struct
guint depth;
guint frame_count;
gint color_table_id;
+ guint par_n;
+ guint par_d;
GstBuffer *codec_data;
} VisualSampleEntry;
@@ -649,4 +658,14 @@ void atom_moov_add_tag (AtomMOOV *moov, guint32 fourcc, guint32 flags,
const guint8 * data, guint size);
void atom_moov_add_blob_tag (AtomMOOV *moov, guint8 *data, guint size);
+void atom_moov_add_3gp_str_tag (AtomMOOV * moov, guint32 fourcc, const gchar * value);
+void atom_moov_add_3gp_uint_tag (AtomMOOV * moov, guint32 fourcc, guint16 value);
+void atom_moov_add_3gp_str_int_tag (AtomMOOV * moov, guint32 fourcc, const gchar * value,
+ gint16 ivalue);
+void atom_moov_add_3gp_tag (AtomMOOV * moov, guint32 fourcc, guint8 * data,
+ guint size);
+
+#define GST_QT_MUX_DEFAULT_TAG_LANGUAGE "eng"
+guint16 language_code (const char * lang);
+
#endif /* __ATOMS_H__ */
diff --git a/gst/qtmux/fourcc.h b/gst/qtmux/fourcc.h
index 3db60036..9b1fe65a 100644
--- a/gst/qtmux/fourcc.h
+++ b/gst/qtmux/fourcc.h
@@ -104,6 +104,7 @@ G_BEGIN_DECLS
#define FOURCC_wave GST_MAKE_FOURCC('w','a','v','e')
#define FOURCC_appl GST_MAKE_FOURCC('a','p','p','l')
#define FOURCC_esds GST_MAKE_FOURCC('e','s','d','s')
+#define FOURCC_pasp GST_MAKE_FOURCC('p','a','s','p')
#define FOURCC_hnti GST_MAKE_FOURCC('h','n','t','i')
#define FOURCC_rtp_ GST_MAKE_FOURCC('r','t','p',' ')
#define FOURCC_sdp_ GST_MAKE_FOURCC('s','d','p',' ')
@@ -181,6 +182,15 @@ G_BEGIN_DECLS
#define FOURCC_titl GST_MAKE_FOURCC('t','i','t','l')
#define FOURCC__cmt GST_MAKE_FOURCC(0xa9, 'c','m','t')
+/* 3gp tags */
+#define FOURCC_dscp GST_MAKE_FOURCC('d','s','c','p')
+#define FOURCC_perf GST_MAKE_FOURCC('p','e','r','f')
+#define FOURCC_auth GST_MAKE_FOURCC('a','u','t','h')
+#define FOURCC_yrrc GST_MAKE_FOURCC('y','r','r','c')
+#define FOURCC_albm GST_MAKE_FOURCC('a','l','b','m')
+#define FOURCC_loci GST_MAKE_FOURCC('l','o','c','i')
+#define FOURCC_kywd GST_MAKE_FOURCC('k','y','w','d')
+
G_END_DECLS
#endif /* __FOURCC_H__ */
diff --git a/gst/qtmux/gstqtmux.c b/gst/qtmux/gstqtmux.c
index 03b0a1a6..8d811f41 100644
--- a/gst/qtmux/gstqtmux.c
+++ b/gst/qtmux/gstqtmux.c
@@ -111,9 +111,11 @@ enum
PROP_FAST_START_TEMP_FILE
};
-#define MDAT_ATOM_HEADER_SIZE 16
+/* some spare for header size as well */
+#define MDAT_LARGE_FILE_LIMIT ((guint64) 1024 * 1024 * 1024 * 2)
+
#define DEFAULT_LARGE_FILE FALSE
-#define DEFAULT_MOVIE_TIMESCALE 600
+#define DEFAULT_MOVIE_TIMESCALE 1000
#define DEFAULT_DO_CTTS FALSE
#define DEFAULT_FAST_START FALSE
#define DEFAULT_FAST_START_TEMP_FILE NULL
@@ -349,8 +351,294 @@ gst_qt_mux_finalize (GObject * object)
G_OBJECT_CLASS (parent_class)->finalize (object);
}
-/* FIXME approach below is pretty Apple/MOV/MP4/iTunes specific,
- * and as such does not comply with e.g. 3GPP specs */
+static void
+gst_qt_mux_add_mp4_tag (GstQTMux * qtmux, const GstTagList * list,
+ const char *tag, const char *tag2, guint32 fourcc)
+{
+ switch (gst_tag_get_type (tag)) {
+ /* strings */
+ case G_TYPE_STRING:
+ {
+ gchar *str = NULL;
+
+ if (!gst_tag_list_get_string (list, tag, &str) || !str)
+ break;
+ GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s",
+ GST_FOURCC_ARGS (fourcc), str);
+ atom_moov_add_str_tag (qtmux->moov, fourcc, str);
+ g_free (str);
+ break;
+ }
+ /* double */
+ case G_TYPE_DOUBLE:
+ {
+ gdouble value;
+
+ if (!gst_tag_list_get_double (list, tag, &value))
+ break;
+ GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %u",
+ GST_FOURCC_ARGS (fourcc), (gint) value);
+ atom_moov_add_uint_tag (qtmux->moov, fourcc, 21, (gint) value);
+ break;
+ }
+ /* paired unsigned integers */
+ case G_TYPE_UINT:
+ {
+ guint value;
+ guint count;
+
+ if (!gst_tag_list_get_uint (list, tag, &value) ||
+ !gst_tag_list_get_uint (list, tag2, &count))
+ break;
+ GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %u/%u",
+ GST_FOURCC_ARGS (fourcc), value, count);
+ atom_moov_add_uint_tag (qtmux->moov, fourcc, 0,
+ value << 16 | (count & 0xFFFF));
+ break;
+ }
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+gst_qt_mux_add_mp4_date (GstQTMux * qtmux, const GstTagList * list,
+ const char *tag, const char *tag2, guint32 fourcc)
+{
+ GDate *date = NULL;
+ GDateYear year;
+ GDateMonth month;
+ GDateDay day;
+ gchar *str;
+
+ g_return_if_fail (gst_tag_get_type (tag) == GST_TYPE_DATE);
+
+ if (!gst_tag_list_get_date (list, tag, &date) || !date)
+ return;
+
+ year = g_date_get_year (date);
+ month = g_date_get_month (date);
+ day = g_date_get_day (date);
+
+ if (year == G_DATE_BAD_YEAR && month == G_DATE_BAD_MONTH &&
+ day == G_DATE_BAD_DAY) {
+ GST_WARNING_OBJECT (qtmux, "invalid date in tag");
+ return;
+ }
+
+ str = g_strdup_printf ("%u-%u-%u", year, month, day);
+ GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s",
+ GST_FOURCC_ARGS (fourcc), str);
+ atom_moov_add_str_tag (qtmux->moov, fourcc, str);
+}
+
+static void
+gst_qt_mux_add_mp4_cover (GstQTMux * qtmux, const GstTagList * list,
+ const char *tag, const char *tag2, guint32 fourcc)
+{
+ GValue value = { 0, };
+ GstBuffer *buf;
+ GstCaps *caps;
+ GstStructure *structure;
+ gint flags = 0;
+
+ g_return_if_fail (gst_tag_get_type (tag) == GST_TYPE_BUFFER);
+
+ if (!gst_tag_list_copy_value (&value, list, tag))
+ return;
+
+ buf = gst_value_get_buffer (&value);
+ if (!buf)
+ goto done;
+
+ caps = gst_buffer_get_caps (buf);
+ if (!caps) {
+ GST_WARNING_OBJECT (qtmux, "preview image without caps");
+ goto done;
+ }
+
+ GST_DEBUG_OBJECT (qtmux, "preview image caps %" GST_PTR_FORMAT, caps);
+
+ structure = gst_caps_get_structure (caps, 0);
+ if (gst_structure_has_name (structure, "image/jpeg"))
+ flags = 13;
+ else if (gst_structure_has_name (structure, "image/png"))
+ flags = 14;
+ gst_caps_unref (caps);
+
+ if (!flags) {
+ GST_WARNING_OBJECT (qtmux, "preview image format not supported");
+ goto done;
+ }
+
+ GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT
+ " -> image size %d", GST_FOURCC_ARGS (fourcc), GST_BUFFER_SIZE (buf));
+ atom_moov_add_tag (qtmux->moov, fourcc, flags, GST_BUFFER_DATA (buf),
+ GST_BUFFER_SIZE (buf));
+done:
+ g_value_unset (&value);
+}
+
+static void
+gst_qt_mux_add_3gp_str (GstQTMux * qtmux, const GstTagList * list,
+ const char *tag, const char *tag2, guint32 fourcc)
+{
+ gchar *str = NULL;
+ guint number;
+
+ g_return_if_fail (gst_tag_get_type (tag) == G_TYPE_STRING);
+ g_return_if_fail (!tag2 || gst_tag_get_type (tag2) == G_TYPE_UINT);
+
+ if (!gst_tag_list_get_string (list, tag, &str) || !str)
+ return;
+
+ if (tag2)
+ if (!gst_tag_list_get_uint (list, tag2, &number))
+ tag2 = NULL;
+
+ if (!tag2) {
+ GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s",
+ GST_FOURCC_ARGS (fourcc), str);
+ atom_moov_add_3gp_str_tag (qtmux->moov, fourcc, str);
+ } else {
+ GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s/%d",
+ GST_FOURCC_ARGS (fourcc), str, number);
+ atom_moov_add_3gp_str_int_tag (qtmux->moov, fourcc, str, number);
+ }
+
+ g_free (str);
+}
+
+static void
+gst_qt_mux_add_3gp_date (GstQTMux * qtmux, const GstTagList * list,
+ const char *tag, const char *tag2, guint32 fourcc)
+{
+ GDate *date = NULL;
+ GDateYear year;
+
+ g_return_if_fail (gst_tag_get_type (tag) == GST_TYPE_DATE);
+
+ if (!gst_tag_list_get_date (list, tag, &date) || !date)
+ return;
+
+ year = g_date_get_year (date);
+
+ if (year == G_DATE_BAD_YEAR) {
+ GST_WARNING_OBJECT (qtmux, "invalid date in tag");
+ return;
+ }
+
+ GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %d", year);
+ atom_moov_add_3gp_uint_tag (qtmux->moov, fourcc, year);
+}
+
+static void
+gst_qt_mux_add_3gp_location (GstQTMux * qtmux, const GstTagList * list,
+ const char *tag, const char *tag2, guint32 fourcc)
+{
+ gdouble latitude = -360, longitude = -360, altitude = 0;
+ gchar *location = NULL;
+ guint8 *data, *ddata;
+ gint size = 0, len = 0;
+ gboolean ret = FALSE;
+
+ g_return_if_fail (strcmp (tag, GST_TAG_GEO_LOCATION_NAME) == 0);
+
+ ret = gst_tag_list_get_string (list, tag, &location);
+ ret |= gst_tag_list_get_double (list, GST_TAG_GEO_LOCATION_LONGITUDE,
+ &longitude);
+ ret |= gst_tag_list_get_double (list, GST_TAG_GEO_LOCATION_LATITUDE,
+ &latitude);
+ ret |= gst_tag_list_get_double (list, GST_TAG_GEO_LOCATION_ELEVATION,
+ &altitude);
+
+ if (!ret)
+ return;
+
+ if (location)
+ len = strlen (location);
+ size += len + 1 + 2;
+
+ /* role + (long, lat, alt) + body + notes */
+ size += 1 + 3 * 4 + 1 + 1;
+
+ data = ddata = g_malloc (size);
+
+ /* language tag */
+ GST_WRITE_UINT16_BE (data, language_code (GST_QT_MUX_DEFAULT_TAG_LANGUAGE));
+ /* location */
+ if (location)
+ memcpy (data + 2, location, len);
+ GST_WRITE_UINT8 (data + 2 + len, 0);
+ data += len + 1 + 2;
+ /* role */
+ GST_WRITE_UINT8 (data, 0);
+ /* long, lat, alt */
+ GST_WRITE_UINT32_BE (data + 1, (guint32) (longitude * 65536.0));
+ GST_WRITE_UINT32_BE (data + 5, (guint32) (latitude * 65536.0));
+ GST_WRITE_UINT32_BE (data + 9, (guint32) (altitude * 65536.0));
+ /* neither astronomical body nor notes */
+ GST_WRITE_UINT16_BE (data + 13, 0);
+
+ GST_DEBUG_OBJECT (qtmux, "Adding tag 'loci'");
+ atom_moov_add_3gp_tag (qtmux->moov, fourcc, ddata, size);
+ g_free (ddata);
+}
+
+static void
+gst_qt_mux_add_3gp_keywords (GstQTMux * qtmux, const GstTagList * list,
+ const char *tag, const char *tag2, guint32 fourcc)
+{
+ gchar *keywords = NULL;
+ guint8 *data, *ddata;
+ gint size = 0, i;
+ gchar **kwds;
+
+ g_return_if_fail (strcmp (tag, GST_TAG_KEYWORDS) == 0);
+
+ if (!gst_tag_list_get_string (list, tag, &keywords) || !keywords)
+ return;
+
+ kwds = g_strsplit (keywords, ",", 0);
+
+ size = 0;
+ for (i = 0; kwds[i]; i++) {
+ /* size byte + null-terminator */
+ size += strlen (kwds[i]) + 1 + 1;
+ }
+
+ /* language tag + count + keywords */
+ size += 2 + 1;
+
+ data = ddata = g_malloc (size);
+
+ /* language tag */
+ GST_WRITE_UINT16_BE (data, language_code (GST_QT_MUX_DEFAULT_TAG_LANGUAGE));
+ /* count */
+ GST_WRITE_UINT8 (data + 2, i);
+ data += 3;
+ /* keywords */
+ for (i = 0; kwds[i]; ++i) {
+ gint len = strlen (kwds[i]);
+
+ GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s",
+ GST_FOURCC_ARGS (fourcc), kwds[i]);
+ /* size */
+ GST_WRITE_UINT8 (data, len + 1);
+ memcpy (data + 1, kwds[i], len + 1);
+ data += len + 2;
+ }
+
+ g_strfreev (kwds);
+
+ atom_moov_add_3gp_tag (qtmux->moov, fourcc, ddata, size);
+ g_free (ddata);
+}
+
+
+typedef void (*GstQTMuxAddTagFunc) (GstQTMux * mux, const GstTagList * list,
+ const char *tag, const char *tag2, guint32 fourcc);
/*
* Struct to record mappings from gstreamer tags to fourcc codes
@@ -360,25 +648,42 @@ typedef struct _GstTagToFourcc
guint32 fourcc;
const gchar *gsttag;
const gchar *gsttag2;
+ const GstQTMuxAddTagFunc func;
} GstTagToFourcc;
/* tag list tags to fourcc matching */
-static const GstTagToFourcc tag_matches[] = {
- {FOURCC__alb, GST_TAG_ALBUM,},
- {FOURCC__ART, GST_TAG_ARTIST,},
- {FOURCC__cmt, GST_TAG_COMMENT,},
- {FOURCC__wrt, GST_TAG_COMPOSER,},
- {FOURCC__gen, GST_TAG_GENRE,},
- {FOURCC__nam, GST_TAG_TITLE,},
- {FOURCC__des, GST_TAG_DESCRIPTION,},
- {FOURCC__too, GST_TAG_ENCODER,},
- {FOURCC_cprt, GST_TAG_COPYRIGHT,},
- {FOURCC_keyw, GST_TAG_KEYWORDS,},
- {FOURCC__day, GST_TAG_DATE,},
- {FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE,},
- {FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT},
- {FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT},
- {FOURCC_covr, GST_TAG_PREVIEW_IMAGE,},
+static const GstTagToFourcc tag_matches_mp4[] = {
+ {FOURCC__alb, GST_TAG_ALBUM, NULL, gst_qt_mux_add_mp4_tag},
+ {FOURCC__ART, GST_TAG_ARTIST, NULL, gst_qt_mux_add_mp4_tag},
+ {FOURCC__cmt, GST_TAG_COMMENT, NULL, gst_qt_mux_add_mp4_tag},
+ {FOURCC__wrt, GST_TAG_COMPOSER, NULL, gst_qt_mux_add_mp4_tag},
+ {FOURCC__gen, GST_TAG_GENRE, NULL, gst_qt_mux_add_mp4_tag},
+ {FOURCC__nam, GST_TAG_TITLE, NULL, gst_qt_mux_add_mp4_tag},
+ {FOURCC__des, GST_TAG_DESCRIPTION, NULL, gst_qt_mux_add_mp4_tag},
+ {FOURCC__too, GST_TAG_ENCODER, NULL, gst_qt_mux_add_mp4_tag},
+ {FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, gst_qt_mux_add_mp4_tag},
+ {FOURCC_keyw, GST_TAG_KEYWORDS, NULL, gst_qt_mux_add_mp4_tag},
+ {FOURCC__day, GST_TAG_DATE, NULL, gst_qt_mux_add_mp4_date},
+ {FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE, NULL, gst_qt_mux_add_mp4_tag},
+ {FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT,
+ gst_qt_mux_add_mp4_tag},
+ {FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
+ gst_qt_mux_add_mp4_tag},
+ {FOURCC_covr, GST_TAG_PREVIEW_IMAGE, NULL, gst_qt_mux_add_mp4_cover},
+ {0, NULL,}
+};
+
+static const GstTagToFourcc tag_matches_3gp[] = {
+ {FOURCC_titl, GST_TAG_TITLE, NULL, gst_qt_mux_add_3gp_str},
+ {FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, gst_qt_mux_add_3gp_str},
+ {FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, gst_qt_mux_add_3gp_str},
+ {FOURCC_perf, GST_TAG_ARTIST, NULL, gst_qt_mux_add_3gp_str},
+ {FOURCC_auth, GST_TAG_COMPOSER, NULL, gst_qt_mux_add_3gp_str},
+ {FOURCC_gnre, GST_TAG_GENRE, NULL, gst_qt_mux_add_3gp_str},
+ {FOURCC_kywd, GST_TAG_KEYWORDS, NULL, gst_qt_mux_add_3gp_keywords},
+ {FOURCC_yrrc, GST_TAG_DATE, NULL, gst_qt_mux_add_3gp_date},
+ {FOURCC_albm, GST_TAG_ALBUM, GST_TAG_TRACK_NUMBER, gst_qt_mux_add_3gp_str},
+ {FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, gst_qt_mux_add_3gp_location},
{0, NULL,}
};
@@ -388,127 +693,35 @@ static const GstTagToFourcc tag_matches[] = {
static void
gst_qt_mux_add_metadata_tags (GstQTMux * qtmux, const GstTagList * list)
{
+ GstQTMuxClass *qtmux_klass = (GstQTMuxClass *) (G_OBJECT_GET_CLASS (qtmux));
guint32 fourcc;
gint i;
const gchar *tag, *tag2;
+ const GstTagToFourcc *tag_matches;
+
+ switch (qtmux_klass->format) {
+ case GST_QT_MUX_FORMAT_3GP:
+ tag_matches = tag_matches_3gp;
+ break;
+ case GST_QT_MUX_FORMAT_MJ2:
+ tag_matches = NULL;
+ break;
+ default:
+ /* sort of iTunes style for mp4 and QT (?) */
+ tag_matches = tag_matches_mp4;
+ break;
+ }
+
+ if (!tag_matches)
+ return;
for (i = 0; tag_matches[i].fourcc; i++) {
fourcc = tag_matches[i].fourcc;
tag = tag_matches[i].gsttag;
tag2 = tag_matches[i].gsttag2;
- switch (gst_tag_get_type (tag)) {
- /* strings */
- case G_TYPE_STRING:
- {
- gchar *str = NULL;
-
- if (!gst_tag_list_get_string (list, tag, &str) || !str)
- break;
- GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s",
- GST_FOURCC_ARGS (fourcc), str);
- atom_moov_add_str_tag (qtmux->moov, fourcc, str);
- g_free (str);
- break;
- }
- /* double */
- case G_TYPE_DOUBLE:
- {
- gdouble value;
-
- if (!gst_tag_list_get_double (list, tag, &value))
- break;
- GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %u",
- GST_FOURCC_ARGS (fourcc), (gint) value);
- atom_moov_add_uint_tag (qtmux->moov, fourcc, 21, (gint) value);
- break;
- }
- /* paired unsigned integers */
- case G_TYPE_UINT:
- {
- guint value;
- guint count;
-
- if (!gst_tag_list_get_uint (list, tag, &value) ||
- !gst_tag_list_get_uint (list, tag2, &count))
- break;
- GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %u/%u",
- GST_FOURCC_ARGS (fourcc), value, count);
- atom_moov_add_uint_tag (qtmux->moov, fourcc, 0,
- value << 16 | (count & 0xFFFF));
- break;
- }
- default:
- {
- if (gst_tag_get_type (tag) == GST_TYPE_DATE) {
- GDate *date = NULL;
- GDateYear year;
- GDateMonth month;
- GDateDay day;
- gchar *str;
-
- if (!gst_tag_list_get_date (list, tag, &date) || !date)
- break;
- year = g_date_get_year (date);
- month = g_date_get_month (date);
- day = g_date_get_day (date);
-
- if (year == G_DATE_BAD_YEAR && month == G_DATE_BAD_MONTH &&
- day == G_DATE_BAD_DAY) {
- GST_WARNING_OBJECT (qtmux, "invalid date in tag");
- break;
- }
-
- str = g_strdup_printf ("%u-%u-%u", year, month, day);
- GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s",
- GST_FOURCC_ARGS (fourcc), str);
- atom_moov_add_str_tag (qtmux->moov, fourcc, str);
- } else if (gst_tag_get_type (tag) == GST_TYPE_BUFFER) {
- GValue value = { 0, };
- GstBuffer *buf;
- GstCaps *caps;
- GstStructure *structure;
- gint flags = 0;
-
- if (!gst_tag_list_copy_value (&value, list, tag))
- break;
-
- buf = gst_value_get_buffer (&value);
- if (!buf)
- goto done;
-
- caps = gst_buffer_get_caps (buf);
- if (!caps) {
- GST_WARNING_OBJECT (qtmux, "preview image without caps");
- goto done;
- }
-
- GST_DEBUG_OBJECT (qtmux, "preview image caps %" GST_PTR_FORMAT, caps);
-
- structure = gst_caps_get_structure (caps, 0);
- if (gst_structure_has_name (structure, "image/jpeg"))
- flags = 13;
- else if (gst_structure_has_name (structure, "image/png"))
- flags = 14;
- gst_caps_unref (caps);
-
- if (!flags) {
- GST_WARNING_OBJECT (qtmux, "preview image format not supported");
- goto done;
- }
-
- GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT
- " -> image size %d", GST_FOURCC_ARGS (fourcc),
- GST_BUFFER_SIZE (buf));
- atom_moov_add_tag (qtmux->moov, fourcc, flags, GST_BUFFER_DATA (buf),
- GST_BUFFER_SIZE (buf));
- done:
- g_value_unset (&value);
- } else
- g_assert_not_reached ();
- break;
- }
- }
+ g_assert (tag_matches[i].func);
+ tag_matches[i].func (qtmux, list, tag, tag2, fourcc);
}
/* add unparsed blobs if present */
@@ -532,8 +745,12 @@ gst_qt_mux_add_metadata_tags (GstQTMux * qtmux, const GstTagList * list)
GST_PTR_FORMAT, i, num_tags, GST_BUFFER_SIZE (buf), caps);
s = gst_caps_get_structure (caps, 0);
if (s && (style = gst_structure_get_string (s, "style"))) {
- /* FIXME make into a parameter */
- if (strcmp (style, "itunes") == 0) {
+ /* try to prevent some style tag ending up into another variant
+ * (todo: make into a list if more cases) */
+ if ((strcmp (style, "itunes") == 0 &&
+ qtmux_klass->format == GST_QT_MUX_FORMAT_MP4) ||
+ (strcmp (style, "iso") == 0 &&
+ qtmux_klass->format == GST_QT_MUX_FORMAT_3GP)) {
GST_DEBUG_OBJECT (qtmux, "Adding private tag");
atom_moov_add_blob_tag (qtmux->moov, GST_BUFFER_DATA (buf),
GST_BUFFER_SIZE (buf));
@@ -600,7 +817,7 @@ gst_qt_mux_send_buffer (GstQTMux * qtmux, GstBuffer * buf, guint64 * offset,
res = gst_pad_push (qtmux->srcpad, buf);
}
- if (offset)
+ if (G_LIKELY (offset))
*offset += size;
return res;
@@ -690,7 +907,8 @@ seek_failed:
* seek back to it later and update when the streams have finished.
*/
static GstFlowReturn
-gst_qt_mux_send_mdat_header (GstQTMux * qtmux, guint64 * off, guint64 size)
+gst_qt_mux_send_mdat_header (GstQTMux * qtmux, guint64 * off, guint64 size,
+ gboolean extended)
{
Atom *node_header;
GstBuffer *buf;
@@ -702,11 +920,15 @@ gst_qt_mux_send_mdat_header (GstQTMux * qtmux, guint64 * off, guint64 size)
node_header = g_malloc0 (sizeof (Atom));
node_header->type = FOURCC_mdat;
- /* use extended size */
- node_header->size = 1;
- node_header->extended_size = 0;
- if (size)
- node_header->extended_size = size;
+ if (extended) {
+ /* use extended size */
+ node_header->size = 1;
+ node_header->extended_size = 0;
+ if (size)
+ node_header->extended_size = size + 16;
+ } else {
+ node_header->size = size + 8;
+ }
size = offset = 0;
if (atom_copy_data (node_header, &data, &size, &offset) == 0)
@@ -740,14 +962,31 @@ gst_qt_mux_update_mdat_size (GstQTMux * qtmux, guint64 mdat_pos,
{
GstEvent *event;
GstBuffer *buf;
+ gboolean large_file;
+
+ large_file = (mdat_size > MDAT_LARGE_FILE_LIMIT);
+
+ if (large_file)
+ mdat_pos += 8;
/* seek and rewrite the header */
event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
mdat_pos, GST_CLOCK_TIME_NONE, 0);
gst_pad_push_event (qtmux->srcpad, event);
- buf = gst_buffer_new_and_alloc (sizeof (guint64));
- GST_WRITE_UINT64_BE (GST_BUFFER_DATA (buf), mdat_size);
+ if (large_file) {
+ buf = gst_buffer_new_and_alloc (sizeof (guint64));
+ GST_WRITE_UINT64_BE (GST_BUFFER_DATA (buf), mdat_size + 16);
+ } else {
+ guint8 *data;
+
+ buf = gst_buffer_new_and_alloc (16);
+ data = GST_BUFFER_DATA (buf);
+ GST_WRITE_UINT32_BE (data, 8);
+ GST_WRITE_UINT32_LE (data + 4, FOURCC_free);
+ GST_WRITE_UINT32_BE (data + 8, mdat_size + 8);
+ GST_WRITE_UINT32_LE (data + 12, FOURCC_mdat);
+ }
return gst_qt_mux_send_buffer (qtmux, buf, offset, FALSE);
}
@@ -798,6 +1037,7 @@ gst_qt_mux_stop_file (GstQTMux * qtmux)
/* tags into file metadata */
gst_qt_mux_setup_metadata (qtmux);
+ large_file = (qtmux->mdat_size > MDAT_LARGE_FILE_LIMIT);
/* if faststart, update the offset of the atoms in the movie with the offset
* that the movie headers before mdat will cause */
if (qtmux->fast_start_file) {
@@ -807,7 +1047,7 @@ gst_qt_mux_stop_file (GstQTMux * qtmux)
goto serialize_error;
GST_DEBUG_OBJECT (qtmux, "calculated moov atom size %" G_GUINT64_FORMAT,
size);
- offset += qtmux->header_size + MDAT_ATOM_HEADER_SIZE;
+ offset += qtmux->header_size + (large_file ? 16 : 8);
} else
offset = qtmux->header_size;
atom_moov_chunks_add_offset (qtmux->moov, offset);
@@ -828,12 +1068,11 @@ gst_qt_mux_stop_file (GstQTMux * qtmux)
GST_DEBUG_OBJECT (qtmux, "Pushing movie atoms");
gst_qt_mux_send_buffer (qtmux, buffer, NULL, FALSE);
- /* total mdat size as of now also includes the atom header */
- qtmux->mdat_size += MDAT_ATOM_HEADER_SIZE;
/* if needed, send mdat atom and move buffered data into it */
if (qtmux->fast_start_file) {
/* mdat size = accumulated (buffered data) + mdat atom header */
- ret = gst_qt_mux_send_mdat_header (qtmux, NULL, qtmux->mdat_size);
+ ret = gst_qt_mux_send_mdat_header (qtmux, NULL, qtmux->mdat_size,
+ large_file);
if (ret != GST_FLOW_OK)
return ret;
ret = gst_qt_mux_send_buffered_data (qtmux, NULL);
@@ -927,9 +1166,9 @@ gst_qt_mux_start_file (GstQTMux * qtmux)
if (!qtmux->fast_start_file)
goto open_failed;
} else {
- ret = gst_qt_mux_send_mdat_header (qtmux, &qtmux->header_size, 0);
- /* mdat size position = current header pos - extended header size */
- qtmux->mdat_pos = qtmux->header_size - sizeof (guint64);
+ /* extended to ensure some spare space */
+ qtmux->mdat_pos = qtmux->header_size;
+ ret = gst_qt_mux_send_mdat_header (qtmux, &qtmux->header_size, 0, TRUE);
}
GST_OBJECT_UNLOCK (qtmux);
@@ -1464,7 +1703,6 @@ gst_qt_mux_video_sink_set_caps (GstPad * pad, GstCaps * caps)
par_den = 1;
gst_structure_get_fraction (structure, "pixel-aspect-ratio", &par_num,
&par_den);
- /* FIXME: pixel-aspect-ratio */
qtpad->is_out_of_order = FALSE;
@@ -1477,6 +1715,8 @@ gst_qt_mux_video_sink_set_caps (GstPad * pad, GstCaps * caps)
/* set common properties */
entry.width = width;
entry.height = height;
+ entry.par_n = par_num;
+ entry.par_d = par_den;
/* should be OK according to qt and iso spec, override if really needed */
entry.color_table_id = -1;
entry.frame_count = 1;
diff --git a/gst/qtmux/gstqtmux.h b/gst/qtmux/gstqtmux.h
index a4701cc1..567773a0 100644
--- a/gst/qtmux/gstqtmux.h
+++ b/gst/qtmux/gstqtmux.h
@@ -109,7 +109,7 @@ struct _GstQTMux
guint64 header_size;
/* accumulated size of raw media data (a priori not including mdat header) */
guint64 mdat_size;
- /* position of mdat extended size field (for later updating) */
+ /* position of mdat atom (for later updating) */
guint64 mdat_pos;
/* atom helper objects */
diff --git a/gst/qtmux/gstqtmuxmap.c b/gst/qtmux/gstqtmuxmap.c
index e0e4faed..808856a2 100644
--- a/gst/qtmux/gstqtmuxmap.c
+++ b/gst/qtmux/gstqtmuxmap.c
@@ -201,6 +201,8 @@ gst_qt_mux_map_format_to_flavor (GstQTMuxFormat format)
{
if (format == GST_QT_MUX_FORMAT_QT)
return ATOMS_TREE_FLAVOR_MOV;
+ else if (format == GST_QT_MUX_FORMAT_3GP)
+ return ATOMS_TREE_FLAVOR_3GP;
else
return ATOMS_TREE_FLAVOR_ISOM;
}
diff --git a/gst/rtpmanager/gstrtpjitterbuffer.c b/gst/rtpmanager/gstrtpjitterbuffer.c
index e023df10..55126054 100644
--- a/gst/rtpmanager/gstrtpjitterbuffer.c
+++ b/gst/rtpmanager/gstrtpjitterbuffer.c
@@ -1688,9 +1688,8 @@ flushing:
}
pause:
{
- const gchar *reason = gst_flow_get_name (result);
-
- GST_DEBUG_OBJECT (jitterbuffer, "pausing task, reason %s", reason);
+ GST_DEBUG_OBJECT (jitterbuffer, "pausing task, reason %s",
+ gst_flow_get_name (result));
JBUF_LOCK (priv);
/* store result */
diff --git a/gst/rtpmanager/gstrtpsession.c b/gst/rtpmanager/gstrtpsession.c
index c33fdfc6..9407ee52 100644
--- a/gst/rtpmanager/gstrtpsession.c
+++ b/gst/rtpmanager/gstrtpsession.c
@@ -259,7 +259,7 @@ struct _GstRtpSessionPrivate
static GstFlowReturn gst_rtp_session_process_rtp (RTPSession * sess,
RTPSource * src, GstBuffer * buffer, gpointer user_data);
static GstFlowReturn gst_rtp_session_send_rtp (RTPSession * sess,
- RTPSource * src, GstBuffer * buffer, gpointer user_data);
+ RTPSource * src, gpointer data, gpointer user_data);
static GstFlowReturn gst_rtp_session_send_rtcp (RTPSession * sess,
RTPSource * src, GstBuffer * buffer, gboolean eos, gpointer user_data);
static GstFlowReturn gst_rtp_session_sync_rtcp (RTPSession * sess,
@@ -1032,8 +1032,8 @@ gst_rtp_session_clear_pt_map (GstRtpSession * rtpsession)
g_hash_table_foreach_remove (rtpsession->priv->ptmap, return_true, NULL);
}
-/* called when the session manager has an RTP packet ready for further
- * processing */
+/* called when the session manager has an RTP packet or a list of packets
+ * ready for further processing */
static GstFlowReturn
gst_rtp_session_process_rtp (RTPSession * sess, RTPSource * src,
GstBuffer * buffer, gpointer user_data)
@@ -1060,7 +1060,7 @@ gst_rtp_session_process_rtp (RTPSession * sess, RTPSource * src,
* sending */
static GstFlowReturn
gst_rtp_session_send_rtp (RTPSession * sess, RTPSource * src,
- GstBuffer * buffer, gpointer user_data)
+ gpointer data, gpointer user_data)
{
GstFlowReturn result;
GstRtpSession *rtpsession;
@@ -1069,12 +1069,17 @@ gst_rtp_session_send_rtp (RTPSession * sess, RTPSource * src,
rtpsession = GST_RTP_SESSION (user_data);
priv = rtpsession->priv;
- GST_LOG_OBJECT (rtpsession, "sending RTP packet");
-
if (rtpsession->send_rtp_src) {
- result = gst_pad_push (rtpsession->send_rtp_src, buffer);
+ if (GST_IS_BUFFER (data)) {
+ GST_LOG_OBJECT (rtpsession, "sending RTP packet");
+ result = gst_pad_push (rtpsession->send_rtp_src, GST_BUFFER_CAST (data));
+ } else {
+ GST_LOG_OBJECT (rtpsession, "sending RTP list");
+ result = gst_pad_push_list (rtpsession->send_rtp_src,
+ GST_BUFFER_LIST_CAST (data));
+ }
} else {
- gst_buffer_unref (buffer);
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
result = GST_FLOW_OK;
}
return result;
@@ -1642,11 +1647,12 @@ gst_rtp_session_setcaps_send_rtp (GstPad * pad, GstCaps * caps)
return TRUE;
}
-/* Recieve an RTP packet to be send to the receivers, send to RTP session
- * manager and forward to send_rtp_src.
+/* Recieve an RTP packet or a list of packets to be send to the receivers,
+ * send to RTP session manager and forward to send_rtp_src.
*/
static GstFlowReturn
-gst_rtp_session_chain_send_rtp (GstPad * pad, GstBuffer * buffer)
+gst_rtp_session_chain_send_rtp_common (GstPad * pad, gpointer data,
+ gboolean is_list)
{
GstRtpSession *rtpsession;
GstRtpSessionPrivate *priv;
@@ -1658,10 +1664,22 @@ gst_rtp_session_chain_send_rtp (GstPad * pad, GstBuffer * buffer)
rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
priv = rtpsession->priv;
- GST_LOG_OBJECT (rtpsession, "received RTP packet");
+ GST_LOG_OBJECT (rtpsession, "received RTP %s", is_list ? "list" : "packet");
/* get NTP time when this packet was captured, this depends on the timestamp. */
- timestamp = GST_BUFFER_TIMESTAMP (buffer);
+ if (is_list) {
+ GstBuffer *buffer = NULL;
+
+ /* All groups in an list have the same timestamp.
+ * So, just take it from the first group. */
+ buffer = gst_buffer_list_get (GST_BUFFER_LIST_CAST (data), 0, 0);
+ if (buffer)
+ timestamp = GST_BUFFER_TIMESTAMP (buffer);
+ else
+ timestamp = -1;
+ } else {
+ timestamp = GST_BUFFER_TIMESTAMP (GST_BUFFER_CAST (data));
+ }
if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
/* convert to running time using the segment start value. */
ntpnstime =
@@ -1676,7 +1694,9 @@ gst_rtp_session_chain_send_rtp (GstPad * pad, GstBuffer * buffer)
}
current_time = gst_clock_get_time (priv->sysclock);
- ret = rtp_session_send_rtp (priv->session, buffer, current_time, ntpnstime);
+ ret =
+ rtp_session_send_rtp (priv->session, data, is_list, current_time,
+ ntpnstime);
if (ret != GST_FLOW_OK)
goto push_error;
@@ -1694,6 +1714,18 @@ push_error:
}
}
+static GstFlowReturn
+gst_rtp_session_chain_send_rtp (GstPad * pad, GstBuffer * buffer)
+{
+ return gst_rtp_session_chain_send_rtp_common (pad, buffer, FALSE);
+}
+
+static GstFlowReturn
+gst_rtp_session_chain_send_rtp_list (GstPad * pad, GstBufferList * list)
+{
+ return gst_rtp_session_chain_send_rtp_common (pad, list, TRUE);
+}
+
/* Create sinkpad to receive RTP packets from senders. This will also create a
* srcpad for the RTP packets.
*/
@@ -1817,6 +1849,8 @@ create_send_rtp_sink (GstRtpSession * rtpsession)
"send_rtp_sink");
gst_pad_set_chain_function (rtpsession->send_rtp_sink,
gst_rtp_session_chain_send_rtp);
+ gst_pad_set_chain_list_function (rtpsession->send_rtp_sink,
+ gst_rtp_session_chain_send_rtp_list);
gst_pad_set_getcaps_function (rtpsession->send_rtp_sink,
gst_rtp_session_getcaps_send_rtp);
gst_pad_set_setcaps_function (rtpsession->send_rtp_sink,
diff --git a/gst/rtpmanager/rtpsession.c b/gst/rtpmanager/rtpsession.c
index 219aacf1..cda04182 100644
--- a/gst/rtpmanager/rtpsession.c
+++ b/gst/rtpmanager/rtpsession.c
@@ -958,7 +958,7 @@ rtp_session_get_sdes_string (RTPSession * sess, GstRTCPSDESType type)
}
static GstFlowReturn
-source_push_rtp (RTPSource * source, GstBuffer * buffer, RTPSession * session)
+source_push_rtp (RTPSource * source, gpointer data, RTPSession * session)
{
GstFlowReturn result = GST_FLOW_OK;
@@ -969,21 +969,21 @@ source_push_rtp (RTPSource * source, GstBuffer * buffer, RTPSession * session)
if (session->callbacks.send_rtp)
result =
- session->callbacks.send_rtp (session, source, buffer,
+ session->callbacks.send_rtp (session, source, data,
session->send_rtp_user_data);
- else
- gst_buffer_unref (buffer);
-
+ else {
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+ }
} else {
GST_LOG ("source %08x pushed receiver RTP packet", source->ssrc);
RTP_SESSION_UNLOCK (session);
if (session->callbacks.process_rtp)
result =
- session->callbacks.process_rtp (session, source, buffer,
- session->process_rtp_user_data);
+ session->callbacks.process_rtp (session, source,
+ GST_BUFFER_CAST (data), session->process_rtp_user_data);
else
- gst_buffer_unref (buffer);
+ gst_buffer_unref (GST_BUFFER_CAST (data));
}
RTP_SESSION_LOCK (session);
@@ -1962,7 +1962,7 @@ ignore:
/**
* rtp_session_send_rtp:
* @sess: an #RTPSession
- * @buffer: an RTP buffer
+ * @data: pointer to either an RTP buffer or a list of RTP buffers
* @current_time: the current system time
* @ntpnstime: the NTP time in nanoseconds of when this buffer was captured.
* This is the buffer timestamp converted to NTP time.
@@ -1973,20 +1973,27 @@ ignore:
* Returns: a #GstFlowReturn.
*/
GstFlowReturn
-rtp_session_send_rtp (RTPSession * sess, GstBuffer * buffer,
+rtp_session_send_rtp (RTPSession * sess, gpointer data, gboolean is_list,
GstClockTime current_time, guint64 ntpnstime)
{
GstFlowReturn result;
RTPSource *source;
gboolean prevsender;
+ gboolean valid_packet;
g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
- g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
+ g_return_val_if_fail (is_list || GST_IS_BUFFER (data), GST_FLOW_ERROR);
- if (!gst_rtp_buffer_validate (buffer))
+ if (is_list) {
+ valid_packet = gst_rtp_buffer_list_validate (GST_BUFFER_LIST_CAST (data));
+ } else {
+ valid_packet = gst_rtp_buffer_validate (GST_BUFFER_CAST (data));
+ }
+
+ if (!valid_packet)
goto invalid_packet;
- GST_LOG ("received RTP packet for sending");
+ GST_LOG ("received RTP %s for sending", is_list ? "list" : "packet");
RTP_SESSION_LOCK (sess);
source = sess->source;
@@ -1997,7 +2004,7 @@ rtp_session_send_rtp (RTPSession * sess, GstBuffer * buffer,
prevsender = RTP_SOURCE_IS_SENDER (source);
/* we use our own source to send */
- result = rtp_source_send_rtp (source, buffer, ntpnstime);
+ result = rtp_source_send_rtp (source, data, is_list, ntpnstime);
if (RTP_SOURCE_IS_SENDER (source) && !prevsender)
sess->stats.sender_sources++;
@@ -2008,7 +2015,7 @@ rtp_session_send_rtp (RTPSession * sess, GstBuffer * buffer,
/* ERRORS */
invalid_packet:
{
- gst_buffer_unref (buffer);
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
GST_DEBUG ("invalid RTP packet received");
return GST_FLOW_OK;
}
diff --git a/gst/rtpmanager/rtpsession.h b/gst/rtpmanager/rtpsession.h
index 7e327a7d..6312f1c1 100644
--- a/gst/rtpmanager/rtpsession.h
+++ b/gst/rtpmanager/rtpsession.h
@@ -65,7 +65,7 @@ typedef GstFlowReturn (*RTPSessionProcessRTP) (RTPSession *sess, RTPSource *src,
*
* Returns: a #GstFlowReturn.
*/
-typedef GstFlowReturn (*RTPSessionSendRTP) (RTPSession *sess, RTPSource *src, GstBuffer *buffer, gpointer user_data);
+typedef GstFlowReturn (*RTPSessionSendRTP) (RTPSession *sess, RTPSource *src, gpointer data, gpointer user_data);
/**
* RTPSessionSendRTCP:
@@ -288,7 +288,7 @@ GstFlowReturn rtp_session_process_rtcp (RTPSession *sess, GstBuffer
GstClockTime current_time);
/* processing packets for sending */
-GstFlowReturn rtp_session_send_rtp (RTPSession *sess, GstBuffer *buffer,
+GstFlowReturn rtp_session_send_rtp (RTPSession *sess, gpointer data, gboolean is_list,
GstClockTime current_time, guint64 ntpnstime);
/* stopping the session */
diff --git a/gst/rtpmanager/rtpsource.c b/gst/rtpmanager/rtpsource.c
index ed080717..209c17b5 100644
--- a/gst/rtpmanager/rtpsource.c
+++ b/gst/rtpmanager/rtpsource.c
@@ -26,7 +26,7 @@
GST_DEBUG_CATEGORY_STATIC (rtp_source_debug);
#define GST_CAT_DEFAULT rtp_source_debug
-#define RTP_MAX_PROBATION_LEN 32
+#define RTP_MAX_PROBATION_LEN 32
/* signals and args */
enum
@@ -1091,41 +1091,73 @@ rtp_source_process_bye (RTPSource * src, const gchar * reason)
src->received_bye = TRUE;
}
+static GstBufferListItem
+set_ssrc (GstBuffer ** buffer, guint group, guint idx, RTPSource * src)
+{
+ *buffer = gst_buffer_make_writable (*buffer);
+ gst_rtp_buffer_set_ssrc (*buffer, src->ssrc);
+ return GST_BUFFER_LIST_SKIP_GROUP;
+}
+
/**
* rtp_source_send_rtp:
* @src: an #RTPSource
- * @buffer: an RTP buffer
+ * @data: an RTP buffer or a list of RTP buffers
+ * @is_list: if @data is a buffer or list
* @ntpnstime: the NTP time when this buffer was captured in nanoseconds. This
* is the buffer timestamp converted to NTP time.
*
- * Send an RTP @buffer originating from @src. This will make @src a sender.
- * This function takes ownership of @buffer and modifies the SSRC in the RTP
- * packet to that of @src when needed.
+ * Send @data (an RTP buffer or list of buffers) originating from @src.
+ * This will make @src a sender. This function takes ownership of @data and
+ * modifies the SSRC in the RTP packet to that of @src when needed.
*
* Returns: a #GstFlowReturn.
*/
GstFlowReturn
-rtp_source_send_rtp (RTPSource * src, GstBuffer * buffer, guint64 ntpnstime)
+rtp_source_send_rtp (RTPSource * src, gpointer data, gboolean is_list,
+ guint64 ntpnstime)
{
- GstFlowReturn result = GST_FLOW_OK;
+ GstFlowReturn result;
guint len;
guint32 rtptime;
guint64 ext_rtptime;
guint64 ntp_diff, rtp_diff;
guint64 elapsed;
+ GstBufferList *list = NULL;
+ GstBuffer *buffer = NULL;
+ guint packets;
+ guint32 ssrc;
g_return_val_if_fail (RTP_IS_SOURCE (src), GST_FLOW_ERROR);
- g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
+ g_return_val_if_fail (is_list || GST_IS_BUFFER (data), GST_FLOW_ERROR);
- len = gst_rtp_buffer_get_payload_len (buffer);
+ if (is_list) {
+ list = GST_BUFFER_LIST_CAST (data);
+ /* We can grab the caps from the first group, since all
+ * groups of a buffer list have same caps. */
+ buffer = gst_buffer_list_get (list, 0, 0);
+ if (!buffer)
+ goto no_buffer;
+ } else {
+ buffer = GST_BUFFER_CAST (data);
+ }
rtp_source_update_caps (src, GST_BUFFER_CAPS (buffer));
/* we are a sender now */
src->is_sender = TRUE;
+ if (is_list) {
+ /* Each group makes up a network packet. */
+ packets = gst_buffer_list_n_groups (list);
+ len = gst_rtp_buffer_list_get_payload_len (list);
+ } else {
+ packets = 1;
+ len = gst_rtp_buffer_get_payload_len (buffer);
+ }
+
/* update stats for the SR */
- src->stats.packets_sent++;
+ src->stats.packets_sent += packets;
src->stats.octets_sent += len;
src->bytes_sent += len;
@@ -1156,7 +1188,11 @@ rtp_source_send_rtp (RTPSource * src, GstBuffer * buffer, guint64 ntpnstime)
src->bitrate = 0;
}
- rtptime = gst_rtp_buffer_get_timestamp (buffer);
+ if (is_list) {
+ rtptime = gst_rtp_buffer_list_get_timestamp (list);
+ } else {
+ rtptime = gst_rtp_buffer_get_timestamp (buffer);
+ }
ext_rtptime = src->last_rtptime;
ext_rtptime = gst_rtp_buffer_ext_timestamp (&ext_rtptime, rtptime);
@@ -1180,31 +1216,53 @@ rtp_source_send_rtp (RTPSource * src, GstBuffer * buffer, guint64 ntpnstime)
src->last_ntpnstime = ntpnstime;
/* push packet */
- if (src->callbacks.push_rtp) {
- guint32 ssrc;
+ if (!src->callbacks.push_rtp)
+ goto no_callback;
+ if (is_list) {
+ ssrc = gst_rtp_buffer_list_get_ssrc (list);
+ } else {
ssrc = gst_rtp_buffer_get_ssrc (buffer);
- if (ssrc != src->ssrc) {
- /* the SSRC of the packet is not correct, make a writable buffer and
- * update the SSRC. This could involve a complete copy of the packet when
- * it is not writable. Usually the payloader will use caps negotiation to
- * get the correct SSRC from the session manager before pushing anything. */
- buffer = gst_buffer_make_writable (buffer);
-
- /* FIXME, we don't want to warn yet because we can't inform any payloader
- * of the changes SSRC yet because we don't implement pad-alloc. */
- GST_LOG ("updating SSRC from %08x to %08x, fix the payloader", ssrc,
- src->ssrc);
- gst_rtp_buffer_set_ssrc (buffer, src->ssrc);
+ }
+
+ if (ssrc != src->ssrc) {
+ /* the SSRC of the packet is not correct, make a writable buffer and
+ * update the SSRC. This could involve a complete copy of the packet when
+ * it is not writable. Usually the payloader will use caps negotiation to
+ * get the correct SSRC from the session manager before pushing anything. */
+
+ /* FIXME, we don't want to warn yet because we can't inform any payloader
+ * of the changes SSRC yet because we don't implement pad-alloc. */
+ GST_LOG ("updating SSRC from %08x to %08x, fix the payloader", ssrc,
+ src->ssrc);
+
+ if (is_list) {
+ list = gst_buffer_list_make_writable (list);
+ gst_buffer_list_foreach (list, (GstBufferListFunc) set_ssrc, src);
+ } else {
+ set_ssrc (&buffer, 0, 0, src);
}
- GST_LOG ("pushing RTP packet %" G_GUINT64_FORMAT, src->stats.packets_sent);
- result = src->callbacks.push_rtp (src, buffer, src->user_data);
- } else {
- GST_WARNING ("no callback installed, dropping packet");
- gst_buffer_unref (buffer);
}
+ GST_LOG ("pushing RTP %s %" G_GUINT64_FORMAT, is_list ? "list" : "packet",
+ src->stats.packets_sent);
+
+ result = src->callbacks.push_rtp (src, data, src->user_data);
return result;
+
+ /* ERRORS */
+no_buffer:
+ {
+ GST_WARNING ("no buffers in buffer list");
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+ return GST_FLOW_OK;
+ }
+no_callback:
+ {
+ GST_WARNING ("no callback installed, dropping packet");
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
+ return GST_FLOW_OK;
+ }
}
/**
diff --git a/gst/rtpmanager/rtpsource.h b/gst/rtpmanager/rtpsource.h
index a44ac1cd..8286f2ec 100644
--- a/gst/rtpmanager/rtpsource.h
+++ b/gst/rtpmanager/rtpsource.h
@@ -194,7 +194,7 @@ void rtp_source_set_rtcp_from (RTPSource *src, GstNetAddress *a
/* handling RTP */
GstFlowReturn rtp_source_process_rtp (RTPSource *src, GstBuffer *buffer, RTPArrivalStats *arrival);
-GstFlowReturn rtp_source_send_rtp (RTPSource *src, GstBuffer *buffer, guint64 ntpnstime);
+GstFlowReturn rtp_source_send_rtp (RTPSource *src, gpointer data, gboolean is_list, guint64 ntpnstime);
/* RTCP messages */
void rtp_source_process_bye (RTPSource *src, const gchar *reason);
diff --git a/gst/scaletempo/gstscaletempo.c b/gst/scaletempo/gstscaletempo.c
index ad3b949a..ed25d132 100644
--- a/gst/scaletempo/gstscaletempo.c
+++ b/gst/scaletempo/gstscaletempo.c
@@ -230,7 +230,7 @@ best_overlap_offset_s16 (GstScaletempo * scaletempo)
ppc = p->buf_pre_corr;
ppc += p->samples_overlap - p->samples_per_frame;
ps += p->samples_overlap - p->samples_per_frame;
- i = -(p->samples_overlap - p->samples_per_frame);
+ i = -((glong) p->samples_overlap - (glong) p->samples_per_frame);
do {
corr += ppc[i + 0] * ps[i + 0];
corr += ppc[i + 1] * ps[i + 1];
diff --git a/gst/selector/gstinputselector.c b/gst/selector/gstinputselector.c
index 08d3d497..832707d0 100644
--- a/gst/selector/gstinputselector.c
+++ b/gst/selector/gstinputselector.c
@@ -420,6 +420,8 @@ gst_selector_pad_event (GstPad * pad, GstEvent * event)
gst_tag_list_free (oldtags);
GST_DEBUG_OBJECT (pad, "received tags %" GST_PTR_FORMAT, newtags);
GST_OBJECT_UNLOCK (selpad);
+
+ g_object_notify (G_OBJECT (selpad), "tags");
break;
}
case GST_EVENT_EOS:
@@ -1415,6 +1417,7 @@ gst_input_selector_check_eos (GstElement * selector)
if (!pad->eos) {
done = TRUE;
}
+ gst_object_unref (pad);
break;
case GST_ITERATOR_RESYNC:
gst_iterator_resync (it);
diff --git a/gst/selector/gstoutputselector.c b/gst/selector/gstoutputselector.c
index bf354a74..15c0a726 100644
--- a/gst/selector/gstoutputselector.c
+++ b/gst/selector/gstoutputselector.c
@@ -196,7 +196,7 @@ gst_output_selector_set_property (GObject * object, guint prop_id,
next_pad = g_value_get_object (value);
- GST_LOG_OBJECT (sel, "Activating pad %s:%s",
+ GST_INFO_OBJECT (sel, "Activating pad %s:%s",
GST_DEBUG_PAD_NAME (next_pad));
GST_OBJECT_LOCK (object);
@@ -262,11 +262,11 @@ gst_output_selector_buffer_alloc (GstPad * pad, guint64 offset, guint size,
GstPad *allocpad;
sel = GST_OUTPUT_SELECTOR (GST_PAD_PARENT (pad));
-
res = GST_FLOW_NOT_LINKED;
GST_OBJECT_LOCK (sel);
- if ((allocpad = sel->active_srcpad)) {
+ allocpad = sel->pending_srcpad ? sel->pending_srcpad : sel->active_srcpad;
+ if (allocpad) {
/* if we had a previous pad we used for allocating a buffer, continue using
* it. */
GST_DEBUG_OBJECT (sel, "using pad %s:%s for alloc",
@@ -278,9 +278,16 @@ gst_output_selector_buffer_alloc (GstPad * pad, guint64 offset, guint size,
gst_object_unref (allocpad);
GST_OBJECT_LOCK (sel);
+ } else {
+ /* fallback case, allocate a buffer of our own, add pad caps. */
+ GST_DEBUG_OBJECT (pad, "fallback buffer alloc");
+ *buf = NULL;
+ res = GST_FLOW_OK;
}
GST_OBJECT_UNLOCK (sel);
+ GST_DEBUG_OBJECT (sel, "buffer alloc finished: %s", gst_flow_get_name (res));
+
return res;
}
@@ -330,14 +337,24 @@ gst_output_selector_release_pad (GstElement * element, GstPad * pad)
static gboolean
gst_output_selector_switch (GstOutputSelector * osel)
{
- gboolean res = TRUE;
+ gboolean res = FALSE;
GstEvent *ev = NULL;
GstSegment *seg = NULL;
gint64 start = 0, position = 0;
+ /* Switch */
+ GST_OBJECT_LOCK (GST_OBJECT (osel));
GST_INFO ("switching to pad %" GST_PTR_FORMAT, osel->pending_srcpad);
-
if (gst_pad_is_linked (osel->pending_srcpad)) {
+ osel->active_srcpad = osel->pending_srcpad;
+ res = TRUE;
+ }
+ gst_object_unref (osel->pending_srcpad);
+ osel->pending_srcpad = NULL;
+ GST_OBJECT_UNLOCK (GST_OBJECT (osel));
+
+ /* Send NEWSEGMENT event and latest buffer if switching succeeded */
+ if (res) {
/* Send NEWSEGMENT to the pad we are going to switch to */
seg = &osel->segment;
/* If resending then mark newsegment start and position accordingly */
@@ -349,29 +366,22 @@ gst_output_selector_switch (GstOutputSelector * osel)
}
ev = gst_event_new_new_segment (TRUE, seg->rate,
seg->format, start, seg->stop, position);
- if (!gst_pad_push_event (osel->pending_srcpad, ev)) {
+ if (!gst_pad_push_event (osel->active_srcpad, ev)) {
GST_WARNING_OBJECT (osel,
"newsegment handling failed in %" GST_PTR_FORMAT,
- osel->pending_srcpad);
+ osel->active_srcpad);
}
/* Resend latest buffer to newly switched pad */
if (osel->resend_latest && osel->latest_buffer) {
GST_INFO ("resending latest buffer");
- gst_pad_push (osel->pending_srcpad, osel->latest_buffer);
+ gst_pad_push (osel->active_srcpad, osel->latest_buffer);
osel->latest_buffer = NULL;
}
-
- /* Switch */
- osel->active_srcpad = osel->pending_srcpad;
} else {
GST_WARNING_OBJECT (osel, "switch failed, pad not linked");
- res = FALSE;
}
- gst_object_unref (osel->pending_srcpad);
- osel->pending_srcpad = NULL;
-
return res;
}
@@ -389,10 +399,15 @@ gst_output_selector_chain (GstPad * pad, GstBuffer * buf)
gst_output_selector_switch (osel);
}
- /* Keep reference to latest buffer to resend it after switch */
- if (osel->latest_buffer)
+ if (osel->latest_buffer) {
gst_buffer_unref (osel->latest_buffer);
- osel->latest_buffer = gst_buffer_ref (buf);
+ osel->latest_buffer = NULL;
+ }
+
+ if (osel->resend_latest) {
+ /* Keep reference to latest buffer to resend it after switch */
+ osel->latest_buffer = gst_buffer_ref (buf);
+ }
/* Keep track of last stop and use it in NEWSEGMENT start after
switching to a new src pad */
diff --git a/gst/shapewipe/gstshapewipe.c b/gst/shapewipe/gstshapewipe.c
index c2c4ce2b..95218801 100644
--- a/gst/shapewipe/gstshapewipe.c
+++ b/gst/shapewipe/gstshapewipe.c
@@ -56,6 +56,11 @@ static void gst_shape_wipe_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_shape_wipe_reset (GstShapeWipe * self);
+static void gst_shape_wipe_update_qos (GstShapeWipe * self, gdouble proportion,
+ GstClockTimeDiff diff, GstClockTime time);
+static void gst_shape_wipe_reset_qos (GstShapeWipe * self);
+static void gst_shape_wipe_read_qos (GstShapeWipe * self, gdouble * proportion,
+ GstClockTime * time);
static GstStateChangeReturn gst_shape_wipe_change_state (GstElement * element,
GstStateChange transition);
@@ -67,6 +72,10 @@ static gboolean gst_shape_wipe_video_sink_event (GstPad * pad,
static gboolean gst_shape_wipe_video_sink_setcaps (GstPad * pad,
GstCaps * caps);
static GstCaps *gst_shape_wipe_video_sink_getcaps (GstPad * pad);
+static GstFlowReturn gst_shape_wipe_video_sink_bufferalloc (GstPad * pad,
+ guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
+static gboolean gst_shape_wipe_video_sink_query (GstPad * pad,
+ GstQuery * query);
static GstFlowReturn gst_shape_wipe_mask_sink_chain (GstPad * pad,
GstBuffer * buffer);
static gboolean gst_shape_wipe_mask_sink_event (GstPad * pad, GstEvent * event);
@@ -74,6 +83,7 @@ static gboolean gst_shape_wipe_mask_sink_setcaps (GstPad * pad, GstCaps * caps);
static GstCaps *gst_shape_wipe_mask_sink_getcaps (GstPad * pad);
static gboolean gst_shape_wipe_src_event (GstPad * pad, GstEvent * event);
static GstCaps *gst_shape_wipe_src_getcaps (GstPad * pad);
+static gboolean gst_shape_wipe_src_query (GstPad * pad, GstQuery * query);
enum
{
@@ -165,6 +175,10 @@ gst_shape_wipe_init (GstShapeWipe * self, GstShapeWipeClass * g_class)
GST_DEBUG_FUNCPTR (gst_shape_wipe_video_sink_setcaps));
gst_pad_set_getcaps_function (self->video_sinkpad,
GST_DEBUG_FUNCPTR (gst_shape_wipe_video_sink_getcaps));
+ gst_pad_set_bufferalloc_function (self->video_sinkpad,
+ GST_DEBUG_FUNCPTR (gst_shape_wipe_video_sink_bufferalloc));
+ gst_pad_set_query_function (self->video_sinkpad,
+ GST_DEBUG_FUNCPTR (gst_shape_wipe_video_sink_query));
gst_element_add_pad (GST_ELEMENT (self), self->video_sinkpad);
self->mask_sinkpad =
@@ -184,6 +198,8 @@ gst_shape_wipe_init (GstShapeWipe * self, GstShapeWipeClass * g_class)
GST_DEBUG_FUNCPTR (gst_shape_wipe_src_event));
gst_pad_set_getcaps_function (self->srcpad,
GST_DEBUG_FUNCPTR (gst_shape_wipe_src_getcaps));
+ gst_pad_set_query_function (self->srcpad,
+ GST_DEBUG_FUNCPTR (gst_shape_wipe_src_query));
gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
self->mask_mutex = g_mutex_new ();
@@ -263,6 +279,28 @@ gst_shape_wipe_reset (GstShapeWipe * self)
self->mask_bpp = 0;
gst_segment_init (&self->segment, GST_FORMAT_TIME);
+
+ gst_shape_wipe_reset_qos (self);
+ self->frame_duration = 0;
+}
+
+static GstFlowReturn
+gst_shape_wipe_video_sink_bufferalloc (GstPad * pad, guint64 offset, guint size,
+ GstCaps * caps, GstBuffer ** buf)
+{
+ GstShapeWipe *self = GST_SHAPE_WIPE (gst_pad_get_parent (pad));
+ GstFlowReturn ret = GST_FLOW_OK;
+
+ GST_DEBUG_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);
+
+ gst_object_unref (self);
+
+ return ret;
}
static gboolean
@@ -272,13 +310,15 @@ gst_shape_wipe_video_sink_setcaps (GstPad * pad, GstCaps * caps)
gboolean ret = TRUE;
GstStructure *s;
gint width, height;
+ gint fps_n, fps_d;
GST_DEBUG_OBJECT (pad, "Setting caps: %" GST_PTR_FORMAT, caps);
s = gst_caps_get_structure (caps, 0);
if (!gst_structure_get_int (s, "width", &width) ||
- !gst_structure_get_int (s, "height", &height)) {
+ !gst_structure_get_int (s, "height", &height) ||
+ !gst_structure_get_fraction (s, "framerate", &fps_n, &fps_d)) {
ret = FALSE;
goto done;
}
@@ -294,6 +334,8 @@ gst_shape_wipe_video_sink_setcaps (GstPad * pad, GstCaps * caps)
g_mutex_unlock (self->mask_mutex);
}
+ self->frame_duration = gst_util_uint64_scale (GST_SECOND, fps_d, fps_n);
+
ret = gst_pad_set_caps (self->srcpad, caps);
done:
@@ -567,6 +609,123 @@ gst_shape_wipe_src_getcaps (GstPad * pad)
return ret;
}
+static gboolean
+gst_shape_wipe_video_sink_query (GstPad * pad, GstQuery * query)
+{
+ GstShapeWipe *self = GST_SHAPE_WIPE (gst_pad_get_parent (pad));
+ gboolean ret;
+ GstPad *peer = gst_pad_get_peer (self->srcpad);
+
+ GST_DEBUG_OBJECT (pad, "Handling query of type '%s'",
+ gst_query_type_get_name (GST_QUERY_TYPE (query)));
+
+ if (!peer) {
+ GST_INFO_OBJECT (pad, "No peer yet");
+ ret = FALSE;
+ } else {
+ ret = gst_pad_query (peer, query);
+ gst_object_unref (peer);
+ }
+
+ gst_object_unref (self);
+ return ret;
+}
+
+static gboolean
+gst_shape_wipe_src_query (GstPad * pad, GstQuery * query)
+{
+ GstShapeWipe *self = GST_SHAPE_WIPE (gst_pad_get_parent (pad));
+ gboolean ret;
+ GstPad *peer = gst_pad_get_peer (self->video_sinkpad);
+
+ GST_DEBUG_OBJECT (pad, "Handling query of type '%s'",
+ gst_query_type_get_name (GST_QUERY_TYPE (query)));
+
+ if (!peer) {
+ GST_INFO_OBJECT (pad, "No peer yet");
+ ret = FALSE;
+ } else {
+ ret = gst_pad_query (peer, query);
+ gst_object_unref (peer);
+ }
+
+ gst_object_unref (self);
+ return ret;
+}
+
+static void
+gst_shape_wipe_update_qos (GstShapeWipe * self, gdouble proportion,
+ GstClockTimeDiff diff, GstClockTime timestamp)
+{
+ GST_OBJECT_LOCK (self);
+ self->proportion = proportion;
+ if (G_LIKELY (timestamp != GST_CLOCK_TIME_NONE)) {
+ if (G_UNLIKELY (diff > 0))
+ self->earliest_time = timestamp + 2 * diff + self->frame_duration;
+ else
+ self->earliest_time = timestamp + diff;
+ } else {
+ self->earliest_time = GST_CLOCK_TIME_NONE;
+ }
+ GST_OBJECT_UNLOCK (self);
+}
+
+static void
+gst_shape_wipe_reset_qos (GstShapeWipe * self)
+{
+ gst_shape_wipe_update_qos (self, 0.5, 0, GST_CLOCK_TIME_NONE);
+}
+
+static void
+gst_shape_wipe_read_qos (GstShapeWipe * self, gdouble * proportion,
+ GstClockTime * time)
+{
+ GST_OBJECT_LOCK (self);
+ *proportion = self->proportion;
+ *time = self->earliest_time;
+ GST_OBJECT_UNLOCK (self);
+}
+
+/* Perform qos calculations before processing the next frame. Returns TRUE if
+ * the frame should be processed, FALSE if the frame can be dropped entirely */
+static gboolean
+gst_shape_wipe_do_qos (GstShapeWipe * self, GstClockTime timestamp)
+{
+ GstClockTime qostime, earliest_time;
+ gdouble proportion;
+
+ /* no timestamp, can't do QoS => process frame */
+ if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp))) {
+ GST_LOG_OBJECT (self, "invalid timestamp, can't do QoS, process frame");
+ return TRUE;
+ }
+
+ /* get latest QoS observation values */
+ gst_shape_wipe_read_qos (self, &proportion, &earliest_time);
+
+ /* skip qos if we have no observation (yet) => process frame */
+ if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (earliest_time))) {
+ GST_LOG_OBJECT (self, "no observation yet, process frame");
+ return TRUE;
+ }
+
+ /* qos is done on running time */
+ qostime = gst_segment_to_running_time (&self->segment, GST_FORMAT_TIME,
+ timestamp);
+
+ /* see how our next timestamp relates to the latest qos timestamp */
+ GST_LOG_OBJECT (self, "qostime %" GST_TIME_FORMAT ", earliest %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (qostime), GST_TIME_ARGS (earliest_time));
+
+ if (qostime != GST_CLOCK_TIME_NONE && qostime <= earliest_time) {
+ GST_DEBUG_OBJECT (self, "we are late, drop frame");
+ return FALSE;
+ }
+
+ GST_LOG_OBJECT (self, "process frame");
+ return TRUE;
+}
+
static GstFlowReturn
gst_shape_wipe_blend_16 (GstShapeWipe * self, GstBuffer * inbuf,
GstBuffer * maskbuf, GstBuffer * outbuf)
@@ -686,6 +845,7 @@ gst_shape_wipe_video_sink_chain (GstPad * pad, GstBuffer * buffer)
GstFlowReturn ret = GST_FLOW_OK;
GstBuffer *mask = NULL, *outbuf = NULL;
GstClockTime timestamp;
+ gboolean new_outbuf = FALSE;
timestamp = GST_BUFFER_TIMESTAMP (buffer);
timestamp =
@@ -711,13 +871,28 @@ gst_shape_wipe_video_sink_chain (GstPad * pad, GstBuffer * buffer)
}
g_mutex_unlock (self->mask_mutex);
- ret =
- gst_pad_alloc_buffer_and_set_caps (self->srcpad, GST_BUFFER_OFFSET_NONE,
- GST_BUFFER_SIZE (buffer), GST_PAD_CAPS (self->srcpad), &outbuf);
- if (G_UNLIKELY (ret != GST_FLOW_OK)) {
+ if (!gst_shape_wipe_do_qos (self, GST_BUFFER_TIMESTAMP (buffer))) {
gst_buffer_unref (buffer);
gst_buffer_unref (mask);
- return ret;
+ return GST_FLOW_OK;
+ }
+
+ /* Try to blend inplace, if it's not possible
+ * get a new buffer from downstream.
+ */
+ if (!gst_buffer_is_writable (buffer)) {
+ ret =
+ gst_pad_alloc_buffer_and_set_caps (self->srcpad, GST_BUFFER_OFFSET_NONE,
+ GST_BUFFER_SIZE (buffer), GST_PAD_CAPS (self->srcpad), &outbuf);
+ if (G_UNLIKELY (ret != GST_FLOW_OK)) {
+ gst_buffer_unref (buffer);
+ gst_buffer_unref (mask);
+ return ret;
+ }
+ gst_buffer_copy_metadata (outbuf, buffer, GST_BUFFER_COPY_ALL);
+ new_outbuf = TRUE;
+ } else {
+ outbuf = buffer;
}
if (self->mask_bpp == 16)
@@ -726,7 +901,9 @@ gst_shape_wipe_video_sink_chain (GstPad * pad, GstBuffer * buffer)
ret = gst_shape_wipe_blend_8 (self, buffer, mask, outbuf);
gst_buffer_unref (mask);
- gst_buffer_unref (buffer);
+ if (new_outbuf)
+ gst_buffer_unref (buffer);
+
if (ret != GST_FLOW_OK) {
gst_buffer_unref (outbuf);
return ret;
@@ -813,6 +990,9 @@ gst_shape_wipe_video_sink_event (GstPad * pad, GstEvent * event)
}
}
/* fall through */
+ case GST_EVENT_FLUSH_STOP:
+ gst_shape_wipe_reset_qos (self);
+ /* fall through */
default:
ret = gst_pad_push_event (self->srcpad, event);
break;
@@ -839,6 +1019,16 @@ gst_shape_wipe_src_event (GstPad * pad, GstEvent * event)
gboolean ret;
switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_QOS:{
+ GstClockTimeDiff diff;
+ GstClockTime timestamp;
+ gdouble proportion;
+
+ gst_event_parse_qos (event, &proportion, &diff, &timestamp);
+
+ gst_shape_wipe_update_qos (self, proportion, diff, timestamp);
+ }
+ /* fall through */
default:
ret = gst_pad_push_event (self->video_sinkpad, event);
break;
diff --git a/gst/shapewipe/gstshapewipe.h b/gst/shapewipe/gstshapewipe.h
index 00ed776e..2cc44831 100644
--- a/gst/shapewipe/gstshapewipe.h
+++ b/gst/shapewipe/gstshapewipe.h
@@ -60,6 +60,10 @@ struct _GstShapeWipe
gint mask_bpp;
gint width, height;
+
+ gdouble proportion;
+ GstClockTime earliest_time;
+ GstClockTime frame_duration;
};
struct _GstShapeWipeClass