diff options
author | Thibault Saunier <tsaunier@igalia.com> | 2021-09-24 16:13:07 -0300 |
---|---|---|
committer | Thibault Saunier <tsaunier@igalia.com> | 2021-09-24 16:13:07 -0300 |
commit | 6c364d96262f4e39951618c17de85cdd2dc769f3 (patch) | |
tree | fb9b22114419dbcb8885fe97e5c6d4a267df7560 /libs | |
parent | b4ca58df7624b005a33e182a511904d7cceea890 (diff) | |
download | gstreamer-6c364d96262f4e39951618c17de85cdd2dc769f3.tar.gz |
Move files from gstreamer into the "subprojects/gstreamer/" subdir
Diffstat (limited to 'libs')
136 files changed, 0 insertions, 67235 deletions
diff --git a/libs/gst/base/README b/libs/gst/base/README deleted file mode 100644 index 7214ce2abe..0000000000 --- a/libs/gst/base/README +++ /dev/null @@ -1,6 +0,0 @@ -Base classes ------------- - -GstBaseSink - FIXME: not much point making it operate in pull mode as a generic - base class I guess... diff --git a/libs/gst/base/base-prelude.h b/libs/gst/base/base-prelude.h deleted file mode 100644 index 87defde34e..0000000000 --- a/libs/gst/base/base-prelude.h +++ /dev/null @@ -1,35 +0,0 @@ -/* GStreamer Base Library - * Copyright (C) 2018 GStreamer developers - * - * base-prelude.h: prelude include header for gst-base library - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_BASE_PRELUDE_H__ -#define __GST_BASE_PRELUDE_H__ - -#include <gst/gst.h> - -#ifndef GST_BASE_API -#ifdef BUILDING_GST_BASE -#define GST_BASE_API GST_API_EXPORT /* from config.h */ -#else -#define GST_BASE_API GST_API_IMPORT -#endif -#endif - -#endif /* __GST_BASE_PRELUDE_H__ */ diff --git a/libs/gst/base/base.h b/libs/gst/base/base.h deleted file mode 100644 index 5ad42c11b2..0000000000 --- a/libs/gst/base/base.h +++ /dev/null @@ -1,44 +0,0 @@ -/* GStreamer - * Copyright (C) 2012 GStreamer developers - * - * base.h: single include header for gst-base library - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_BASE_H__ -#define __GST_BASE_H__ - -#include <gst/base/base-prelude.h> - -#include <gst/base/gstadapter.h> -#include <gst/base/gstaggregator.h> -#include <gst/base/gstbaseparse.h> -#include <gst/base/gstbasesink.h> -#include <gst/base/gstbasesrc.h> -#include <gst/base/gstbasetransform.h> -#include <gst/base/gstbitreader.h> -#include <gst/base/gstbitwriter.h> -#include <gst/base/gstbytereader.h> -#include <gst/base/gstbytewriter.h> -#include <gst/base/gstcollectpads.h> -#include <gst/base/gstdataqueue.h> -#include <gst/base/gstflowcombiner.h> -#include <gst/base/gstpushsrc.h> -#include <gst/base/gstqueuearray.h> -#include <gst/base/gsttypefindhelper.h> - -#endif /* __GST_BASE_H__ */ diff --git a/libs/gst/base/gstadapter.c b/libs/gst/base/gstadapter.c deleted file mode 100644 index 33c84f25a1..0000000000 --- a/libs/gst/base/gstadapter.c +++ /dev/null @@ -1,1792 +0,0 @@ -/* GStreamer - * Copyright (C) 2004 Benjamin Otte <otte@gnome.org> - * 2005 Wim Taymans <wim@fluendo.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * SECTION:gstadapter - * @title: GstAdapter - * @short_description: adapts incoming data on a sink pad into chunks of N bytes - * - * This class is for elements that receive buffers in an undesired size. - * While for example raw video contains one image per buffer, the same is not - * true for a lot of other formats, especially those that come directly from - * a file. So if you have undefined buffer sizes and require a specific size, - * this object is for you. - * - * An adapter is created with gst_adapter_new(). It can be freed again with - * g_object_unref(). - * - * The theory of operation is like this: All buffers received are put - * into the adapter using gst_adapter_push() and the data is then read back - * in chunks of the desired size using gst_adapter_map()/gst_adapter_unmap() - * and/or gst_adapter_copy(). After the data has been processed, it is freed - * using gst_adapter_unmap(). - * - * Other methods such as gst_adapter_take() and gst_adapter_take_buffer() - * combine gst_adapter_map() and gst_adapter_unmap() in one method and are - * potentially more convenient for some use cases. - * - * For example, a sink pad's chain function that needs to pass data to a library - * in 512-byte chunks could be implemented like this: - * |[<!-- language="C" --> - * static GstFlowReturn - * sink_pad_chain (GstPad *pad, GstObject *parent, GstBuffer *buffer) - * { - * MyElement *this; - * GstAdapter *adapter; - * GstFlowReturn ret = GST_FLOW_OK; - * - * this = MY_ELEMENT (parent); - * - * adapter = this->adapter; - * - * // put buffer into adapter - * gst_adapter_push (adapter, buffer); - * - * // while we can read out 512 bytes, process them - * while (gst_adapter_available (adapter) >= 512 && ret == GST_FLOW_OK) { - * const guint8 *data = gst_adapter_map (adapter, 512); - * // use flowreturn as an error value - * ret = my_library_foo (data); - * gst_adapter_unmap (adapter); - * gst_adapter_flush (adapter, 512); - * } - * return ret; - * } - * ]| - * - * For another example, a simple element inside GStreamer that uses #GstAdapter - * is the libvisual element. - * - * An element using #GstAdapter in its sink pad chain function should ensure that - * when the FLUSH_STOP event is received, that any queued data is cleared using - * gst_adapter_clear(). Data should also be cleared or processed on EOS and - * when changing state from %GST_STATE_PAUSED to %GST_STATE_READY. - * - * Also check the GST_BUFFER_FLAG_DISCONT flag on the buffer. Some elements might - * need to clear the adapter after a discontinuity. - * - * The adapter will keep track of the timestamps of the buffers - * that were pushed. The last seen timestamp before the current position - * can be queried with gst_adapter_prev_pts(). This function can - * optionally return the number of bytes between the start of the buffer that - * carried the timestamp and the current adapter position. The distance is - * useful when dealing with, for example, raw audio samples because it allows - * you to calculate the timestamp of the current adapter position by using the - * last seen timestamp and the amount of bytes since. Additionally, the - * gst_adapter_prev_pts_at_offset() can be used to determine the last - * seen timestamp at a particular offset in the adapter. - * - * The adapter will also keep track of the offset of the buffers - * (#GST_BUFFER_OFFSET) that were pushed. The last seen offset before the - * current position can be queried with gst_adapter_prev_offset(). This function - * can optionally return the number of bytes between the start of the buffer - * that carried the offset and the current adapter position. - * - * Additionally the adapter also keeps track of the PTS, DTS and buffer offset - * at the last discontinuity, which can be retrieved with - * gst_adapter_pts_at_discont(), gst_adapter_dts_at_discont() and - * gst_adapter_offset_at_discont(). The number of bytes that were consumed - * since then can be queried with gst_adapter_distance_from_discont(). - * - * A last thing to note is that while #GstAdapter is pretty optimized, - * merging buffers still might be an operation that requires a `malloc()` and - * `memcpy()` operation, and these operations are not the fastest. Because of - * this, some functions like gst_adapter_available_fast() are provided to help - * speed up such cases should you want to. To avoid repeated memory allocations, - * gst_adapter_copy() can be used to copy data into a (statically allocated) - * user provided buffer. - * - * #GstAdapter is not MT safe. All operations on an adapter must be serialized by - * the caller. This is not normally a problem, however, as the normal use case - * of #GstAdapter is inside one pad's chain function, in which case access is - * serialized via the pad's STREAM_LOCK. - * - * Note that gst_adapter_push() takes ownership of the buffer passed. Use - * gst_buffer_ref() before pushing it into the adapter if you still want to - * access the buffer later. The adapter will never modify the data in the - * buffer pushed in it. - */ - -#include <gst/gst_private.h> -#include "gstadapter.h" -#include <string.h> -#include <gst/base/gstqueuearray.h> - -/* default size for the assembled data buffer */ -#define DEFAULT_SIZE 4096 - -static void gst_adapter_flush_unchecked (GstAdapter * adapter, gsize flush); - -GST_DEBUG_CATEGORY_STATIC (gst_adapter_debug); -#define GST_CAT_DEFAULT gst_adapter_debug - -struct _GstAdapter -{ - GObject object; - - /*< private > */ - GstQueueArray *bufqueue; - gsize size; - gsize skip; - guint count; - - /* we keep state of assembled pieces */ - gpointer assembled_data; - gsize assembled_size; - gsize assembled_len; - - GstClockTime pts; - guint64 pts_distance; - GstClockTime dts; - guint64 dts_distance; - guint64 offset; - guint64 offset_distance; - - gsize scan_offset; - /* G_MAXUINT when unset */ - guint scan_entry_idx; - - GstClockTime pts_at_discont; - GstClockTime dts_at_discont; - guint64 offset_at_discont; - - guint64 distance_from_discont; - - GstMapInfo info; -}; - -struct _GstAdapterClass -{ - GObjectClass parent_class; -}; - -#define _do_init \ - GST_DEBUG_CATEGORY_INIT (gst_adapter_debug, "adapter", 0, "object to splice and merge buffers to desired size") -#define gst_adapter_parent_class parent_class -G_DEFINE_TYPE_WITH_CODE (GstAdapter, gst_adapter, G_TYPE_OBJECT, _do_init); - -static void gst_adapter_dispose (GObject * object); -static void gst_adapter_finalize (GObject * object); - -static void -gst_adapter_class_init (GstAdapterClass * klass) -{ - GObjectClass *object = G_OBJECT_CLASS (klass); - - object->dispose = gst_adapter_dispose; - object->finalize = gst_adapter_finalize; -} - -static void -gst_adapter_init (GstAdapter * adapter) -{ - adapter->assembled_data = g_malloc (DEFAULT_SIZE); - adapter->assembled_size = DEFAULT_SIZE; - adapter->pts = GST_CLOCK_TIME_NONE; - adapter->pts_distance = 0; - adapter->dts = GST_CLOCK_TIME_NONE; - adapter->dts_distance = 0; - adapter->offset = GST_BUFFER_OFFSET_NONE; - adapter->offset_distance = 0; - adapter->pts_at_discont = GST_CLOCK_TIME_NONE; - adapter->dts_at_discont = GST_CLOCK_TIME_NONE; - adapter->offset_at_discont = GST_BUFFER_OFFSET_NONE; - adapter->distance_from_discont = 0; - adapter->bufqueue = gst_queue_array_new (10); -} - -static void -gst_adapter_dispose (GObject * object) -{ - GstAdapter *adapter = GST_ADAPTER (object); - - gst_adapter_clear (adapter); - - GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object)); -} - -static void -gst_adapter_finalize (GObject * object) -{ - GstAdapter *adapter = GST_ADAPTER (object); - - g_free (adapter->assembled_data); - - gst_queue_array_free (adapter->bufqueue); - - GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); -} - -/** - * gst_adapter_new: - * - * Creates a new #GstAdapter. Free with g_object_unref(). - * - * Returns: (transfer full): a new #GstAdapter - */ -GstAdapter * -gst_adapter_new (void) -{ - return g_object_new (GST_TYPE_ADAPTER, NULL); -} - -/** - * gst_adapter_clear: - * @adapter: a #GstAdapter - * - * Removes all buffers from @adapter. - */ -void -gst_adapter_clear (GstAdapter * adapter) -{ - GstMiniObject *obj; - g_return_if_fail (GST_IS_ADAPTER (adapter)); - - if (adapter->info.memory) - gst_adapter_unmap (adapter); - - while ((obj = gst_queue_array_pop_head (adapter->bufqueue))) - gst_mini_object_unref (obj); - - adapter->count = 0; - adapter->size = 0; - adapter->skip = 0; - adapter->assembled_len = 0; - adapter->pts = GST_CLOCK_TIME_NONE; - adapter->pts_distance = 0; - adapter->dts = GST_CLOCK_TIME_NONE; - adapter->dts_distance = 0; - adapter->offset = GST_BUFFER_OFFSET_NONE; - adapter->offset_distance = 0; - adapter->pts_at_discont = GST_CLOCK_TIME_NONE; - adapter->dts_at_discont = GST_CLOCK_TIME_NONE; - adapter->offset_at_discont = GST_BUFFER_OFFSET_NONE; - adapter->distance_from_discont = 0; - adapter->scan_offset = 0; - adapter->scan_entry_idx = G_MAXUINT; -} - -static inline void -update_timestamps_and_offset (GstAdapter * adapter, GstBuffer * buf) -{ - GstClockTime pts, dts; - guint64 offset; - - pts = GST_BUFFER_PTS (buf); - if (GST_CLOCK_TIME_IS_VALID (pts)) { - GST_LOG_OBJECT (adapter, "new pts %" GST_TIME_FORMAT, GST_TIME_ARGS (pts)); - adapter->pts = pts; - adapter->pts_distance = 0; - } - dts = GST_BUFFER_DTS (buf); - if (GST_CLOCK_TIME_IS_VALID (dts)) { - GST_LOG_OBJECT (adapter, "new dts %" GST_TIME_FORMAT, GST_TIME_ARGS (dts)); - adapter->dts = dts; - adapter->dts_distance = 0; - } - offset = GST_BUFFER_OFFSET (buf); - if (offset != GST_BUFFER_OFFSET_NONE) { - GST_LOG_OBJECT (adapter, "new offset %" G_GUINT64_FORMAT, offset); - adapter->offset = offset; - adapter->offset_distance = 0; - } - - if (GST_BUFFER_IS_DISCONT (buf)) { - /* Take values as-is (might be NONE) */ - adapter->pts_at_discont = pts; - adapter->dts_at_discont = dts; - adapter->offset_at_discont = offset; - adapter->distance_from_discont = 0; - } -} - -/* copy data into @dest, skipping @skip bytes from the head buffers */ -static void -copy_into_unchecked (GstAdapter * adapter, guint8 * dest, gsize skip, - gsize size) -{ - GstBuffer *buf; - gsize bsize, csize; - guint idx = 0; - - /* first step, do skipping */ - /* we might well be copying where we were scanning */ - if (adapter->scan_entry_idx != G_MAXUINT && (adapter->scan_offset <= skip)) { - idx = adapter->scan_entry_idx; - skip -= adapter->scan_offset; - } else { - idx = 0; - } - buf = gst_queue_array_peek_nth (adapter->bufqueue, idx++); - bsize = gst_buffer_get_size (buf); - while (G_UNLIKELY (skip >= bsize)) { - skip -= bsize; - buf = gst_queue_array_peek_nth (adapter->bufqueue, idx++); - bsize = gst_buffer_get_size (buf); - } - /* copy partial buffer */ - csize = MIN (bsize - skip, size); - GST_DEBUG ("bsize %" G_GSIZE_FORMAT ", skip %" G_GSIZE_FORMAT ", csize %" - G_GSIZE_FORMAT, bsize, skip, csize); - GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, adapter, "extract %" G_GSIZE_FORMAT - " bytes", csize); - gst_buffer_extract (buf, skip, dest, csize); - size -= csize; - dest += csize; - - /* second step, copy remainder */ - while (size > 0) { - buf = gst_queue_array_peek_nth (adapter->bufqueue, idx++); - bsize = gst_buffer_get_size (buf); - if (G_LIKELY (bsize > 0)) { - csize = MIN (bsize, size); - GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, adapter, - "extract %" G_GSIZE_FORMAT " bytes", csize); - gst_buffer_extract (buf, 0, dest, csize); - size -= csize; - dest += csize; - } - } -} - -/** - * gst_adapter_push: - * @adapter: a #GstAdapter - * @buf: (transfer full): a #GstBuffer to add to queue in the adapter - * - * Adds the data from @buf to the data stored inside @adapter and takes - * ownership of the buffer. - */ -void -gst_adapter_push (GstAdapter * adapter, GstBuffer * buf) -{ - gsize size; - - g_return_if_fail (GST_IS_ADAPTER (adapter)); - g_return_if_fail (GST_IS_BUFFER (buf)); - - size = gst_buffer_get_size (buf); - adapter->size += size; - - /* Note: merging buffers at this point is premature. */ - if (gst_queue_array_is_empty (adapter->bufqueue)) { - GST_LOG_OBJECT (adapter, "pushing %p first %" G_GSIZE_FORMAT " bytes", - buf, size); - gst_queue_array_push_tail (adapter->bufqueue, buf); - update_timestamps_and_offset (adapter, buf); - } else { - /* Otherwise append to the end, and advance our end pointer */ - GST_LOG_OBJECT (adapter, "pushing %p %" G_GSIZE_FORMAT " bytes at end, " - "size now %" G_GSIZE_FORMAT, buf, size, adapter->size); - gst_queue_array_push_tail (adapter->bufqueue, buf); - } - ++adapter->count; -} - -#if 0 -/* Internal method only. Tries to merge buffers at the head of the queue - * to form a single larger buffer of size 'size'. - * - * Returns %TRUE if it managed to merge anything. - */ -static gboolean -gst_adapter_try_to_merge_up (GstAdapter * adapter, gsize size) -{ - GstBuffer *cur, *head; - GSList *g; - gboolean ret = FALSE; - gsize hsize; - - g = adapter->buflist; - if (g == NULL) - return FALSE; - - head = g->data; - - hsize = gst_buffer_get_size (head); - - /* Remove skipped part from the buffer (otherwise the buffer might grow indefinitely) */ - head = gst_buffer_make_writable (head); - gst_buffer_resize (head, adapter->skip, hsize - adapter->skip); - hsize -= adapter->skip; - adapter->skip = 0; - g->data = head; - - g = g_slist_next (g); - - while (g != NULL && hsize < size) { - cur = g->data; - /* Merge the head buffer and the next in line */ - GST_LOG_OBJECT (adapter, "Merging buffers of size %" G_GSIZE_FORMAT " & %" - G_GSIZE_FORMAT " in search of target %" G_GSIZE_FORMAT, - hsize, gst_buffer_get_size (cur), size); - - head = gst_buffer_append (head, cur); - hsize = gst_buffer_get_size (head); - ret = TRUE; - - /* Delete the front list item, and store our new buffer in the 2nd list - * item */ - adapter->buflist = g_slist_delete_link (adapter->buflist, adapter->buflist); - g->data = head; - - /* invalidate scan position */ - adapter->scan_offset = 0; - adapter->scan_entry = NULL; - - g = g_slist_next (g); - } - - return ret; -} -#endif - -/** - * gst_adapter_map: - * @adapter: a #GstAdapter - * @size: the number of bytes to map/peek - * - * Gets the first @size bytes stored in the @adapter. The returned pointer is - * valid until the next function is called on the adapter. - * - * Note that setting the returned pointer as the data of a #GstBuffer is - * incorrect for general-purpose plugins. The reason is that if a downstream - * element stores the buffer so that it has access to it outside of the bounds - * of its chain function, the buffer will have an invalid data pointer after - * your element flushes the bytes. In that case you should use - * gst_adapter_take(), which returns a freshly-allocated buffer that you can set - * as #GstBuffer memory or the potentially more performant - * gst_adapter_take_buffer(). - * - * Returns %NULL if @size bytes are not available. - * - * Returns: (transfer none) (array length=size) (element-type guint8) (nullable): - * a pointer to the first @size bytes of data, or %NULL - */ -gconstpointer -gst_adapter_map (GstAdapter * adapter, gsize size) -{ - GstBuffer *cur; - gsize skip, csize; - gsize toreuse, tocopy; - guint8 *data; - - g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); - g_return_val_if_fail (size > 0, NULL); - - if (adapter->info.memory) - gst_adapter_unmap (adapter); - - /* we don't have enough data, return NULL. This is unlikely - * as one usually does an _available() first instead of peeking a - * random size. */ - if (G_UNLIKELY (size > adapter->size)) - return NULL; - - /* we have enough assembled data, return it */ - if (adapter->assembled_len >= size) - return adapter->assembled_data; - -#if 0 - do { -#endif - cur = gst_queue_array_peek_head (adapter->bufqueue); - skip = adapter->skip; - - csize = gst_buffer_get_size (cur); - if (csize >= size + skip) { - if (!gst_buffer_map (cur, &adapter->info, GST_MAP_READ)) - return FALSE; - - return (guint8 *) adapter->info.data + skip; - } - /* We may be able to efficiently merge buffers in our pool to - * gather a big enough chunk to return it from the head buffer directly */ -#if 0 - } while (gst_adapter_try_to_merge_up (adapter, size)); -#endif - - /* see how much data we can reuse from the assembled memory and how much - * we need to copy */ - toreuse = adapter->assembled_len; - tocopy = size - toreuse; - - /* Gonna need to copy stuff out */ - if (G_UNLIKELY (adapter->assembled_size < size)) { - adapter->assembled_size = (size / DEFAULT_SIZE + 1) * DEFAULT_SIZE; - GST_DEBUG_OBJECT (adapter, "resizing internal buffer to %" G_GSIZE_FORMAT, - adapter->assembled_size); - if (toreuse == 0) { - GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "alloc new buffer"); - /* no g_realloc to avoid a memcpy that is not desired here since we are - * not going to reuse any data here */ - g_free (adapter->assembled_data); - adapter->assembled_data = g_malloc (adapter->assembled_size); - } else { - /* we are going to reuse all data, realloc then */ - GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "reusing %" G_GSIZE_FORMAT " bytes", - toreuse); - adapter->assembled_data = - g_realloc (adapter->assembled_data, adapter->assembled_size); - } - } - GST_CAT_DEBUG (GST_CAT_PERFORMANCE, "copy remaining %" G_GSIZE_FORMAT - " bytes from adapter", tocopy); - data = adapter->assembled_data; - copy_into_unchecked (adapter, data + toreuse, skip + toreuse, tocopy); - adapter->assembled_len = size; - - return adapter->assembled_data; -} - -/** - * gst_adapter_unmap: - * @adapter: a #GstAdapter - * - * Releases the memory obtained with the last gst_adapter_map(). - */ -void -gst_adapter_unmap (GstAdapter * adapter) -{ - g_return_if_fail (GST_IS_ADAPTER (adapter)); - - if (adapter->info.memory) { - GstBuffer *cur = gst_queue_array_peek_head (adapter->bufqueue); - GST_LOG_OBJECT (adapter, "unmap memory buffer %p", cur); - gst_buffer_unmap (cur, &adapter->info); - adapter->info.memory = NULL; - } -} - -/** - * gst_adapter_copy: (skip) - * @adapter: a #GstAdapter - * @dest: (out caller-allocates) (array length=size) (element-type guint8): - * the memory to copy into - * @offset: the bytes offset in the adapter to start from - * @size: the number of bytes to copy - * - * Copies @size bytes of data starting at @offset out of the buffers - * contained in #GstAdapter into an array @dest provided by the caller. - * - * The array @dest should be large enough to contain @size bytes. - * The user should check that the adapter has (@offset + @size) bytes - * available before calling this function. - */ -void -gst_adapter_copy (GstAdapter * adapter, gpointer dest, gsize offset, gsize size) -{ - g_return_if_fail (GST_IS_ADAPTER (adapter)); - g_return_if_fail (size > 0); - g_return_if_fail (offset + size <= adapter->size); - - copy_into_unchecked (adapter, dest, offset + adapter->skip, size); -} - -/** - * gst_adapter_copy_bytes: (rename-to gst_adapter_copy) - * @adapter: a #GstAdapter - * @offset: the bytes offset in the adapter to start from - * @size: the number of bytes to copy - * - * Similar to gst_adapter_copy, but more suitable for language bindings. @size - * bytes of data starting at @offset will be copied out of the buffers contained - * in @adapter and into a new #GBytes structure which is returned. Depending on - * the value of the @size argument an empty #GBytes structure may be returned. - * - * Returns: (transfer full): A new #GBytes structure containing the copied data. - * - * Since: 1.4 - */ -GBytes * -gst_adapter_copy_bytes (GstAdapter * adapter, gsize offset, gsize size) -{ - gpointer data; - data = g_malloc (size); - gst_adapter_copy (adapter, data, offset, size); - return g_bytes_new_take (data, size); -} - -/*Flushes the first @flush bytes in the @adapter*/ -static void -gst_adapter_flush_unchecked (GstAdapter * adapter, gsize flush) -{ - GstBuffer *cur; - gsize size; - - GST_LOG_OBJECT (adapter, "flushing %" G_GSIZE_FORMAT " bytes", flush); - - if (adapter->info.memory) - gst_adapter_unmap (adapter); - - /* clear state */ - adapter->size -= flush; - adapter->assembled_len = 0; - - /* take skip into account */ - flush += adapter->skip; - /* distance is always at least the amount of skipped bytes */ - adapter->pts_distance -= adapter->skip; - adapter->dts_distance -= adapter->skip; - adapter->offset_distance -= adapter->skip; - adapter->distance_from_discont -= adapter->skip; - - cur = gst_queue_array_peek_head (adapter->bufqueue); - size = gst_buffer_get_size (cur); - while (flush >= size) { - /* can skip whole buffer */ - GST_LOG_OBJECT (adapter, "flushing out head buffer"); - adapter->pts_distance += size; - adapter->dts_distance += size; - adapter->offset_distance += size; - adapter->distance_from_discont += size; - flush -= size; - - --adapter->count; - - cur = NULL; - gst_buffer_unref (gst_queue_array_pop_head (adapter->bufqueue)); - - if (gst_queue_array_is_empty (adapter->bufqueue)) { - GST_LOG_OBJECT (adapter, "adapter empty now"); - break; - } - /* there is a new head buffer, update the timestamps */ - cur = gst_queue_array_peek_head (adapter->bufqueue); - update_timestamps_and_offset (adapter, cur); - size = gst_buffer_get_size (cur); - } - /* account for the remaining bytes */ - adapter->skip = flush; - adapter->pts_distance += flush; - adapter->dts_distance += flush; - adapter->offset_distance += flush; - adapter->distance_from_discont += flush; - /* invalidate scan position */ - adapter->scan_offset = 0; - adapter->scan_entry_idx = G_MAXUINT; -} - -/** - * gst_adapter_flush: - * @adapter: a #GstAdapter - * @flush: the number of bytes to flush - * - * Flushes the first @flush bytes in the @adapter. The caller must ensure that - * at least this many bytes are available. - * - * See also: gst_adapter_map(), gst_adapter_unmap() - */ -void -gst_adapter_flush (GstAdapter * adapter, gsize flush) -{ - g_return_if_fail (GST_IS_ADAPTER (adapter)); - g_return_if_fail (flush <= adapter->size); - - /* flushing out 0 bytes will do nothing */ - if (G_UNLIKELY (flush == 0)) - return; - - gst_adapter_flush_unchecked (adapter, flush); -} - -/* internal function, nbytes should be flushed if needed after calling this function */ -static guint8 * -gst_adapter_get_internal (GstAdapter * adapter, gsize nbytes) -{ - guint8 *data; - gsize toreuse, tocopy; - - /* see how much data we can reuse from the assembled memory and how much - * we need to copy */ - toreuse = MIN (nbytes, adapter->assembled_len); - tocopy = nbytes - toreuse; - - /* find memory to return */ - if (adapter->assembled_size >= nbytes && toreuse > 0) { - /* we reuse already allocated memory but only when we're going to reuse - * something from it because else we are worse than the malloc and copy - * case below */ - GST_LOG_OBJECT (adapter, "reusing %" G_GSIZE_FORMAT " bytes of assembled" - " data", toreuse); - /* we have enough free space in the assembled array */ - data = adapter->assembled_data; - /* flush after this function should set the assembled_size to 0 */ - adapter->assembled_data = g_malloc (adapter->assembled_size); - } else { - GST_LOG_OBJECT (adapter, "allocating %" G_GSIZE_FORMAT " bytes", nbytes); - /* not enough bytes in the assembled array, just allocate new space */ - data = g_malloc (nbytes); - /* reuse what we can from the already assembled data */ - if (toreuse) { - GST_LOG_OBJECT (adapter, "reusing %" G_GSIZE_FORMAT " bytes", toreuse); - GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, adapter, - "memcpy %" G_GSIZE_FORMAT " bytes", toreuse); - memcpy (data, adapter->assembled_data, toreuse); - } - } - if (tocopy) { - /* copy the remaining data */ - copy_into_unchecked (adapter, toreuse + data, toreuse + adapter->skip, - tocopy); - } - return data; -} - -/** - * gst_adapter_take: - * @adapter: a #GstAdapter - * @nbytes: the number of bytes to take - * - * Returns a freshly allocated buffer containing the first @nbytes bytes of the - * @adapter. The returned bytes will be flushed from the adapter. - * - * Caller owns returned value. g_free after usage. - * - * Free-function: g_free - * - * Returns: (transfer full) (array length=nbytes) (element-type guint8) (nullable): - * oven-fresh hot data, or %NULL if @nbytes bytes are not available - */ -gpointer -gst_adapter_take (GstAdapter * adapter, gsize nbytes) -{ - gpointer data; - - g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); - g_return_val_if_fail (nbytes > 0, NULL); - - /* we don't have enough data, return NULL. This is unlikely - * as one usually does an _available() first instead of peeking a - * random size. */ - if (G_UNLIKELY (nbytes > adapter->size)) - return NULL; - - data = gst_adapter_get_internal (adapter, nbytes); - - gst_adapter_flush_unchecked (adapter, nbytes); - - return data; -} - -/** - * gst_adapter_get_buffer_fast: - * @adapter: a #GstAdapter - * @nbytes: the number of bytes to get - * - * Returns a #GstBuffer containing the first @nbytes of the @adapter, but - * does not flush them from the adapter. See gst_adapter_take_buffer_fast() - * for details. - * - * Caller owns a reference to the returned buffer. gst_buffer_unref() after - * usage. - * - * Free-function: gst_buffer_unref - * - * Returns: (transfer full) (nullable): a #GstBuffer containing the first - * @nbytes of the adapter, or %NULL if @nbytes bytes are not available. - * gst_buffer_unref() when no longer needed. - * - * Since: 1.6 - */ -GstBuffer * -gst_adapter_get_buffer_fast (GstAdapter * adapter, gsize nbytes) -{ - GstBuffer *buffer = NULL; - GstBuffer *cur; - gsize skip; - gsize left = nbytes; - guint idx, len; - - g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); - g_return_val_if_fail (nbytes > 0, NULL); - - GST_LOG_OBJECT (adapter, "getting buffer of %" G_GSIZE_FORMAT " bytes", - nbytes); - - /* we don't have enough data, return NULL. This is unlikely - * as one usually does an _available() first instead of grabbing a - * random size. */ - if (G_UNLIKELY (nbytes > adapter->size)) - return NULL; - - skip = adapter->skip; - cur = gst_queue_array_peek_head (adapter->bufqueue); - - if (skip == 0 && gst_buffer_get_size (cur) == nbytes) { - GST_LOG_OBJECT (adapter, "providing buffer of %" G_GSIZE_FORMAT " bytes" - " as head buffer", nbytes); - buffer = gst_buffer_ref (cur); - goto done; - } - - len = gst_queue_array_get_length (adapter->bufqueue); - - for (idx = 0; idx < len && left > 0; idx++) { - gsize size, cur_size; - - cur = gst_queue_array_peek_nth (adapter->bufqueue, idx); - cur_size = gst_buffer_get_size (cur); - size = MIN (cur_size - skip, left); - - GST_LOG_OBJECT (adapter, "appending %" G_GSIZE_FORMAT " bytes" - " via region copy", size); - if (buffer) - gst_buffer_copy_into (buffer, cur, - GST_BUFFER_COPY_MEMORY | GST_BUFFER_COPY_META, skip, size); - else - buffer = gst_buffer_copy_region (cur, GST_BUFFER_COPY_ALL, skip, size); - skip = 0; - left -= size; - } - -done: - - return buffer; -} - -/** - * gst_adapter_take_buffer_fast: - * @adapter: a #GstAdapter - * @nbytes: the number of bytes to take - * - * Returns a #GstBuffer containing the first @nbytes of the @adapter. - * The returned bytes will be flushed from the adapter. This function - * is potentially more performant than gst_adapter_take_buffer() since - * it can reuse the memory in pushed buffers by subbuffering or - * merging. Unlike gst_adapter_take_buffer(), the returned buffer may - * be composed of multiple non-contiguous #GstMemory objects, no - * copies are made. - * - * Note that no assumptions should be made as to whether certain buffer - * flags such as the DISCONT flag are set on the returned buffer, or not. - * The caller needs to explicitly set or unset flags that should be set or - * unset. - * - * This will also copy over all GstMeta of the input buffers except - * for meta with the %GST_META_FLAG_POOLED flag or with the "memory" tag. - * - * This function can return buffer up to the return value of - * gst_adapter_available() without making copies if possible. - * - * Caller owns a reference to the returned buffer. gst_buffer_unref() after - * usage. - * - * Free-function: gst_buffer_unref - * - * Returns: (transfer full) (nullable): a #GstBuffer containing the first - * @nbytes of the adapter, or %NULL if @nbytes bytes are not available. - * gst_buffer_unref() when no longer needed. - * - * Since: 1.2 - */ -GstBuffer * -gst_adapter_take_buffer_fast (GstAdapter * adapter, gsize nbytes) -{ - GstBuffer *buffer; - - g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); - g_return_val_if_fail (nbytes > 0, NULL); - - buffer = gst_adapter_get_buffer_fast (adapter, nbytes); - if (buffer) - gst_adapter_flush_unchecked (adapter, nbytes); - - return buffer; -} - -static gboolean -foreach_metadata (GstBuffer * inbuf, GstMeta ** meta, gpointer user_data) -{ - GstBuffer *outbuf = user_data; - const GstMetaInfo *info = (*meta)->info; - gboolean do_copy = FALSE; - - if (gst_meta_api_type_has_tag (info->api, _gst_meta_tag_memory)) { - /* never call the transform_meta with memory specific metadata */ - GST_DEBUG ("not copying memory specific metadata %s", - g_type_name (info->api)); - do_copy = FALSE; - } else { - do_copy = TRUE; - GST_DEBUG ("copying metadata %s", g_type_name (info->api)); - } - - if (do_copy && info->transform_func) { - GstMetaTransformCopy copy_data = { FALSE, 0, -1 }; - GST_DEBUG ("copy metadata %s", g_type_name (info->api)); - /* simply copy then */ - info->transform_func (outbuf, *meta, inbuf, - _gst_meta_transform_copy, ©_data); - } - return TRUE; -} - -/** - * gst_adapter_get_buffer: - * @adapter: a #GstAdapter - * @nbytes: the number of bytes to get - * - * Returns a #GstBuffer containing the first @nbytes of the @adapter, but - * does not flush them from the adapter. See gst_adapter_take_buffer() - * for details. - * - * Caller owns a reference to the returned buffer. gst_buffer_unref() after - * usage. - * - * Free-function: gst_buffer_unref - * - * Returns: (transfer full) (nullable): a #GstBuffer containing the first - * @nbytes of the adapter, or %NULL if @nbytes bytes are not available. - * gst_buffer_unref() when no longer needed. - * - * Since: 1.6 - */ -GstBuffer * -gst_adapter_get_buffer (GstAdapter * adapter, gsize nbytes) -{ - GstBuffer *buffer; - GstBuffer *cur; - gsize hsize, skip; - guint8 *data; - - g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); - g_return_val_if_fail (nbytes > 0, NULL); - - GST_LOG_OBJECT (adapter, "getting buffer of %" G_GSIZE_FORMAT " bytes", - nbytes); - - /* we don't have enough data, return NULL. This is unlikely - * as one usually does an _available() first instead of grabbing a - * random size. */ - if (G_UNLIKELY (nbytes > adapter->size)) - return NULL; - - cur = gst_queue_array_peek_head (adapter->bufqueue); - skip = adapter->skip; - hsize = gst_buffer_get_size (cur); - - /* our head buffer has enough data left, return it */ - if (skip == 0 && hsize == nbytes) { - GST_LOG_OBJECT (adapter, "providing buffer of %" G_GSIZE_FORMAT " bytes" - " as head buffer", nbytes); - buffer = gst_buffer_ref (cur); - goto done; - } else if (hsize >= nbytes + skip) { - GST_LOG_OBJECT (adapter, "providing buffer of %" G_GSIZE_FORMAT " bytes" - " via region copy", nbytes); - buffer = gst_buffer_copy_region (cur, GST_BUFFER_COPY_ALL, skip, nbytes); - goto done; - } -#if 0 - if (gst_adapter_try_to_merge_up (adapter, nbytes)) { - /* Merged something, let's try again for sub-buffering */ - cur = adapter->buflist->data; - skip = adapter->skip; - if (gst_buffer_get_size (cur) >= nbytes + skip) { - GST_LOG_OBJECT (adapter, "providing buffer of %" G_GSIZE_FORMAT " bytes" - " via sub-buffer", nbytes); - buffer = gst_buffer_copy_region (cur, GST_BUFFER_COPY_ALL, skip, nbytes); - goto done; - } - } -#endif - - data = gst_adapter_get_internal (adapter, nbytes); - - buffer = gst_buffer_new_wrapped (data, nbytes); - - { - guint idx, len; - GstBuffer *cur; - gsize read_offset = 0; - - idx = 0; - len = gst_queue_array_get_length (adapter->bufqueue); - - while (idx < len && read_offset < nbytes + adapter->skip) { - cur = gst_queue_array_peek_nth (adapter->bufqueue, idx); - - gst_buffer_foreach_meta (cur, foreach_metadata, buffer); - read_offset += gst_buffer_get_size (cur); - - idx++; - } - } - -done: - - return buffer; -} - -/** - * gst_adapter_take_buffer: - * @adapter: a #GstAdapter - * @nbytes: the number of bytes to take - * - * Returns a #GstBuffer containing the first @nbytes bytes of the - * @adapter. The returned bytes will be flushed from the adapter. - * This function is potentially more performant than - * gst_adapter_take() since it can reuse the memory in pushed buffers - * by subbuffering or merging. This function will always return a - * buffer with a single memory region. - * - * Note that no assumptions should be made as to whether certain buffer - * flags such as the DISCONT flag are set on the returned buffer, or not. - * The caller needs to explicitly set or unset flags that should be set or - * unset. - * - * Since 1.6 this will also copy over all GstMeta of the input buffers except - * for meta with the %GST_META_FLAG_POOLED flag or with the "memory" tag. - * - * Caller owns a reference to the returned buffer. gst_buffer_unref() after - * usage. - * - * Free-function: gst_buffer_unref - * - * Returns: (transfer full) (nullable): a #GstBuffer containing the first - * @nbytes of the adapter, or %NULL if @nbytes bytes are not available. - * gst_buffer_unref() when no longer needed. - */ -GstBuffer * -gst_adapter_take_buffer (GstAdapter * adapter, gsize nbytes) -{ - GstBuffer *buffer; - - g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); - g_return_val_if_fail (nbytes > 0, NULL); - - buffer = gst_adapter_get_buffer (adapter, nbytes); - if (buffer) - gst_adapter_flush_unchecked (adapter, nbytes); - - return buffer; -} - -/** - * gst_adapter_take_list: - * @adapter: a #GstAdapter - * @nbytes: the number of bytes to take - * - * Returns a #GList of buffers containing the first @nbytes bytes of the - * @adapter. The returned bytes will be flushed from the adapter. - * When the caller can deal with individual buffers, this function is more - * performant because no memory should be copied. - * - * Caller owns returned list and contained buffers. gst_buffer_unref() each - * buffer in the list before freeing the list after usage. - * - * Returns: (element-type Gst.Buffer) (transfer full) (nullable): a #GList of - * buffers containing the first @nbytes of the adapter, or %NULL if @nbytes - * bytes are not available - */ -GList * -gst_adapter_take_list (GstAdapter * adapter, gsize nbytes) -{ - GQueue queue = G_QUEUE_INIT; - GstBuffer *cur; - gsize hsize, skip, cur_size; - - g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); - g_return_val_if_fail (nbytes <= adapter->size, NULL); - - GST_LOG_OBJECT (adapter, "taking %" G_GSIZE_FORMAT " bytes", nbytes); - - while (nbytes > 0) { - cur = gst_queue_array_peek_head (adapter->bufqueue); - skip = adapter->skip; - cur_size = gst_buffer_get_size (cur); - hsize = MIN (nbytes, cur_size - skip); - - cur = gst_adapter_take_buffer (adapter, hsize); - - g_queue_push_tail (&queue, cur); - - nbytes -= hsize; - } - return queue.head; -} - -/** - * gst_adapter_get_list: - * @adapter: a #GstAdapter - * @nbytes: the number of bytes to get - * - * Returns a #GList of buffers containing the first @nbytes bytes of the - * @adapter, but does not flush them from the adapter. See - * gst_adapter_take_list() for details. - * - * Caller owns returned list and contained buffers. gst_buffer_unref() each - * buffer in the list before freeing the list after usage. - * - * Returns: (element-type Gst.Buffer) (transfer full) (nullable): a #GList of - * buffers containing the first @nbytes of the adapter, or %NULL if @nbytes - * bytes are not available - * - * Since: 1.6 - */ -GList * -gst_adapter_get_list (GstAdapter * adapter, gsize nbytes) -{ - GQueue queue = G_QUEUE_INIT; - GstBuffer *cur, *buffer; - gsize hsize, skip, cur_size; - guint idx; - - g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); - g_return_val_if_fail (nbytes <= adapter->size, NULL); - - GST_LOG_OBJECT (adapter, "getting %" G_GSIZE_FORMAT " bytes", nbytes); - - idx = 0; - skip = adapter->skip; - - while (nbytes > 0) { - cur = gst_queue_array_peek_nth (adapter->bufqueue, idx++); - cur_size = gst_buffer_get_size (cur); - hsize = MIN (nbytes, cur_size - skip); - - if (skip == 0 && cur_size == hsize) { - GST_LOG_OBJECT (adapter, - "inserting a buffer of %" G_GSIZE_FORMAT " bytes", hsize); - buffer = gst_buffer_ref (cur); - } else { - GST_LOG_OBJECT (adapter, "inserting a buffer of %" G_GSIZE_FORMAT " bytes" - " via region copy", hsize); - buffer = gst_buffer_copy_region (cur, GST_BUFFER_COPY_ALL, skip, hsize); - } - - g_queue_push_tail (&queue, buffer); - - nbytes -= hsize; - skip = 0; - } - - return queue.head; -} - -/** - * gst_adapter_take_buffer_list: - * @adapter: a #GstAdapter - * @nbytes: the number of bytes to take - * - * Returns a #GstBufferList of buffers containing the first @nbytes bytes of - * the @adapter. The returned bytes will be flushed from the adapter. - * When the caller can deal with individual buffers, this function is more - * performant because no memory should be copied. - * - * Caller owns the returned list. Call gst_buffer_list_unref() to free - * the list after usage. - * - * Returns: (transfer full) (nullable): a #GstBufferList of buffers containing - * the first @nbytes of the adapter, or %NULL if @nbytes bytes are not - * available - * - * Since: 1.6 - */ -GstBufferList * -gst_adapter_take_buffer_list (GstAdapter * adapter, gsize nbytes) -{ - GstBufferList *buffer_list; - GstBuffer *cur; - gsize hsize, skip, cur_size; - guint n_bufs; - - g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); - - if (nbytes > adapter->size) - return NULL; - - GST_LOG_OBJECT (adapter, "taking %" G_GSIZE_FORMAT " bytes", nbytes); - - /* try to create buffer list with sufficient size, so no resize is done later */ - if (adapter->count < 64) - n_bufs = adapter->count; - else - n_bufs = (adapter->count * nbytes * 1.2 / adapter->size) + 1; - - buffer_list = gst_buffer_list_new_sized (n_bufs); - - while (nbytes > 0) { - cur = gst_queue_array_peek_head (adapter->bufqueue); - skip = adapter->skip; - cur_size = gst_buffer_get_size (cur); - hsize = MIN (nbytes, cur_size - skip); - - gst_buffer_list_add (buffer_list, gst_adapter_take_buffer (adapter, hsize)); - nbytes -= hsize; - } - return buffer_list; -} - -/** - * gst_adapter_get_buffer_list: - * @adapter: a #GstAdapter - * @nbytes: the number of bytes to get - * - * Returns a #GstBufferList of buffers containing the first @nbytes bytes of - * the @adapter but does not flush them from the adapter. See - * gst_adapter_take_buffer_list() for details. - * - * Caller owns the returned list. Call gst_buffer_list_unref() to free - * the list after usage. - * - * Returns: (transfer full) (nullable): a #GstBufferList of buffers containing - * the first @nbytes of the adapter, or %NULL if @nbytes bytes are not - * available - * - * Since: 1.6 - */ -GstBufferList * -gst_adapter_get_buffer_list (GstAdapter * adapter, gsize nbytes) -{ - GstBufferList *buffer_list; - GstBuffer *cur, *buffer; - gsize hsize, skip, cur_size; - guint n_bufs; - guint idx; - - g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); - - if (nbytes > adapter->size) - return NULL; - - GST_LOG_OBJECT (adapter, "getting %" G_GSIZE_FORMAT " bytes", nbytes); - - /* try to create buffer list with sufficient size, so no resize is done later */ - if (adapter->count < 64) - n_bufs = adapter->count; - else - n_bufs = (adapter->count * nbytes * 1.2 / adapter->size) + 1; - - buffer_list = gst_buffer_list_new_sized (n_bufs); - - idx = 0; - skip = adapter->skip; - - while (nbytes > 0) { - cur = gst_queue_array_peek_nth (adapter->bufqueue, idx++); - cur_size = gst_buffer_get_size (cur); - hsize = MIN (nbytes, cur_size - skip); - - if (skip == 0 && cur_size == hsize) { - GST_LOG_OBJECT (adapter, - "inserting a buffer of %" G_GSIZE_FORMAT " bytes", hsize); - buffer = gst_buffer_ref (cur); - } else { - GST_LOG_OBJECT (adapter, "inserting a buffer of %" G_GSIZE_FORMAT " bytes" - " via region copy", hsize); - buffer = gst_buffer_copy_region (cur, GST_BUFFER_COPY_ALL, skip, hsize); - } - - gst_buffer_list_add (buffer_list, buffer); - - nbytes -= hsize; - skip = 0; - } - - return buffer_list; -} - -/** - * gst_adapter_available: - * @adapter: a #GstAdapter - * - * Gets the maximum amount of bytes available, that is it returns the maximum - * value that can be supplied to gst_adapter_map() without that function - * returning %NULL. - * - * Returns: number of bytes available in @adapter - */ -gsize -gst_adapter_available (GstAdapter * adapter) -{ - g_return_val_if_fail (GST_IS_ADAPTER (adapter), 0); - - return adapter->size; -} - -/** - * gst_adapter_available_fast: - * @adapter: a #GstAdapter - * - * Gets the maximum number of bytes that are immediately available without - * requiring any expensive operations (like copying the data into a - * temporary buffer). - * - * Returns: number of bytes that are available in @adapter without expensive - * operations - */ -gsize -gst_adapter_available_fast (GstAdapter * adapter) -{ - GstBuffer *cur; - gsize size; - guint idx; - - g_return_val_if_fail (GST_IS_ADAPTER (adapter), 0); - - /* no data */ - if (adapter->size == 0) - return 0; - - /* some stuff we already assembled */ - if (adapter->assembled_len) - return adapter->assembled_len; - - /* take the first non-zero buffer */ - idx = 0; - while (TRUE) { - cur = gst_queue_array_peek_nth (adapter->bufqueue, idx++); - size = gst_buffer_get_size (cur); - if (size != 0) - break; - } - - /* we can quickly get the (remaining) data of the first buffer */ - return size - adapter->skip; -} - -/** - * gst_adapter_distance_from_discont: - * @adapter: a #GstAdapter - * - * Get the distance in bytes since the last buffer with the - * %GST_BUFFER_FLAG_DISCONT flag. - * - * The distance will be reset to 0 for all buffers with - * %GST_BUFFER_FLAG_DISCONT on them, and then calculated for all other - * following buffers based on their size. - * - * Since: 1.10 - * - * Returns: The offset. Can be %GST_BUFFER_OFFSET_NONE. - */ -guint64 -gst_adapter_distance_from_discont (GstAdapter * adapter) -{ - return adapter->distance_from_discont; -} - -/** - * gst_adapter_offset_at_discont: - * @adapter: a #GstAdapter - * - * Get the offset that was on the last buffer with the GST_BUFFER_FLAG_DISCONT - * flag, or GST_BUFFER_OFFSET_NONE. - * - * Since: 1.10 - * - * Returns: The offset at the last discont or GST_BUFFER_OFFSET_NONE. - */ -guint64 -gst_adapter_offset_at_discont (GstAdapter * adapter) -{ - g_return_val_if_fail (GST_IS_ADAPTER (adapter), GST_BUFFER_OFFSET_NONE); - - return adapter->offset_at_discont; -} - -/** - * gst_adapter_pts_at_discont: - * @adapter: a #GstAdapter - * - * Get the PTS that was on the last buffer with the GST_BUFFER_FLAG_DISCONT - * flag, or GST_CLOCK_TIME_NONE. - * - * Since: 1.10 - * - * Returns: The PTS at the last discont or GST_CLOCK_TIME_NONE. - */ -GstClockTime -gst_adapter_pts_at_discont (GstAdapter * adapter) -{ - g_return_val_if_fail (GST_IS_ADAPTER (adapter), GST_CLOCK_TIME_NONE); - - return adapter->pts_at_discont; -} - -/** - * gst_adapter_dts_at_discont: - * @adapter: a #GstAdapter - * - * Get the DTS that was on the last buffer with the GST_BUFFER_FLAG_DISCONT - * flag, or GST_CLOCK_TIME_NONE. - * - * Since: 1.10 - * - * Returns: The DTS at the last discont or GST_CLOCK_TIME_NONE. - */ -GstClockTime -gst_adapter_dts_at_discont (GstAdapter * adapter) -{ - g_return_val_if_fail (GST_IS_ADAPTER (adapter), GST_CLOCK_TIME_NONE); - - return adapter->dts_at_discont; -} - -/** - * gst_adapter_prev_offset: - * @adapter: a #GstAdapter - * @distance: (out) (allow-none): pointer to a location for distance, or %NULL - * - * Get the offset that was before the current byte in the adapter. When - * @distance is given, the amount of bytes between the offset and the current - * position is returned. - * - * The offset is reset to GST_BUFFER_OFFSET_NONE and the distance is set to 0 - * when the adapter is first created or when it is cleared. This also means that - * before the first byte with an offset is removed from the adapter, the offset - * and distance returned are GST_BUFFER_OFFSET_NONE and 0 respectively. - * - * Since: 1.10 - * - * Returns: The previous seen offset. - */ -guint64 -gst_adapter_prev_offset (GstAdapter * adapter, guint64 * distance) -{ - g_return_val_if_fail (GST_IS_ADAPTER (adapter), GST_BUFFER_OFFSET_NONE); - - if (distance) - *distance = adapter->offset_distance; - - return adapter->offset; -} - -/** - * gst_adapter_prev_pts: - * @adapter: a #GstAdapter - * @distance: (out) (allow-none): pointer to location for distance, or %NULL - * - * Get the pts that was before the current byte in the adapter. When - * @distance is given, the amount of bytes between the pts and the current - * position is returned. - * - * The pts is reset to GST_CLOCK_TIME_NONE and the distance is set to 0 when - * the adapter is first created or when it is cleared. This also means that before - * the first byte with a pts is removed from the adapter, the pts - * and distance returned are GST_CLOCK_TIME_NONE and 0 respectively. - * - * Returns: The previously seen pts. - */ -GstClockTime -gst_adapter_prev_pts (GstAdapter * adapter, guint64 * distance) -{ - g_return_val_if_fail (GST_IS_ADAPTER (adapter), GST_CLOCK_TIME_NONE); - - if (distance) - *distance = adapter->pts_distance; - - return adapter->pts; -} - -/** - * gst_adapter_prev_dts: - * @adapter: a #GstAdapter - * @distance: (out) (allow-none): pointer to location for distance, or %NULL - * - * Get the dts that was before the current byte in the adapter. When - * @distance is given, the amount of bytes between the dts and the current - * position is returned. - * - * The dts is reset to GST_CLOCK_TIME_NONE and the distance is set to 0 when - * the adapter is first created or when it is cleared. This also means that before - * the first byte with a dts is removed from the adapter, the dts - * and distance returned are GST_CLOCK_TIME_NONE and 0 respectively. - * - * Returns: The previously seen dts. - */ -GstClockTime -gst_adapter_prev_dts (GstAdapter * adapter, guint64 * distance) -{ - g_return_val_if_fail (GST_IS_ADAPTER (adapter), GST_CLOCK_TIME_NONE); - - if (distance) - *distance = adapter->dts_distance; - - return adapter->dts; -} - -/** - * gst_adapter_prev_pts_at_offset: - * @adapter: a #GstAdapter - * @offset: the offset in the adapter at which to get timestamp - * @distance: (out) (allow-none): pointer to location for distance, or %NULL - * - * Get the pts that was before the byte at offset @offset in the adapter. When - * @distance is given, the amount of bytes between the pts and the current - * position is returned. - * - * The pts is reset to GST_CLOCK_TIME_NONE and the distance is set to 0 when - * the adapter is first created or when it is cleared. This also means that before - * the first byte with a pts is removed from the adapter, the pts - * and distance returned are GST_CLOCK_TIME_NONE and 0 respectively. - * - * Since: 1.2 - * Returns: The previously seen pts at given offset. - */ -GstClockTime -gst_adapter_prev_pts_at_offset (GstAdapter * adapter, gsize offset, - guint64 * distance) -{ - GstBuffer *cur; - gsize read_offset = 0; - gsize pts_offset = 0; - GstClockTime pts = adapter->pts; - guint idx, len; - - g_return_val_if_fail (GST_IS_ADAPTER (adapter), GST_CLOCK_TIME_NONE); - - idx = 0; - len = gst_queue_array_get_length (adapter->bufqueue); - - while (idx < len && read_offset < offset + adapter->skip) { - cur = gst_queue_array_peek_nth (adapter->bufqueue, idx++); - - if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_PTS (cur))) { - pts = GST_BUFFER_PTS (cur); - pts_offset = read_offset; - } - - read_offset += gst_buffer_get_size (cur); - } - - if (distance) - *distance = adapter->pts_distance + offset - pts_offset; - - return pts; -} - -/** - * gst_adapter_prev_dts_at_offset: - * @adapter: a #GstAdapter - * @offset: the offset in the adapter at which to get timestamp - * @distance: (out) (allow-none): pointer to location for distance, or %NULL - * - * Get the dts that was before the byte at offset @offset in the adapter. When - * @distance is given, the amount of bytes between the dts and the current - * position is returned. - * - * The dts is reset to GST_CLOCK_TIME_NONE and the distance is set to 0 when - * the adapter is first created or when it is cleared. This also means that before - * the first byte with a dts is removed from the adapter, the dts - * and distance returned are GST_CLOCK_TIME_NONE and 0 respectively. - * - * Since: 1.2 - * Returns: The previously seen dts at given offset. - */ -GstClockTime -gst_adapter_prev_dts_at_offset (GstAdapter * adapter, gsize offset, - guint64 * distance) -{ - GstBuffer *cur; - gsize read_offset = 0; - gsize dts_offset = 0; - GstClockTime dts = adapter->dts; - guint idx, len; - - g_return_val_if_fail (GST_IS_ADAPTER (adapter), GST_CLOCK_TIME_NONE); - - idx = 0; - len = gst_queue_array_get_length (adapter->bufqueue); - - while (idx < len && read_offset < offset + adapter->skip) { - cur = gst_queue_array_peek_nth (adapter->bufqueue, idx++); - - if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DTS (cur))) { - dts = GST_BUFFER_DTS (cur); - dts_offset = read_offset; - } - - read_offset += gst_buffer_get_size (cur); - } - - if (distance) - *distance = adapter->dts_distance + offset - dts_offset; - - return dts; -} - -/** - * gst_adapter_masked_scan_uint32_peek: - * @adapter: a #GstAdapter - * @mask: mask to apply to data before matching against @pattern - * @pattern: pattern to match (after mask is applied) - * @offset: offset into the adapter data from which to start scanning, returns - * the last scanned position. - * @size: number of bytes to scan from offset - * @value: (out) (allow-none): pointer to uint32 to return matching data - * - * Scan for pattern @pattern with applied mask @mask in the adapter data, - * starting from offset @offset. If a match is found, the value that matched - * is returned through @value, otherwise @value is left untouched. - * - * The bytes in @pattern and @mask are interpreted left-to-right, regardless - * of endianness. All four bytes of the pattern must be present in the - * adapter for it to match, even if the first or last bytes are masked out. - * - * It is an error to call this function without making sure that there is - * enough data (offset+size bytes) in the adapter. - * - * Returns: offset of the first match, or -1 if no match was found. - */ -gssize -gst_adapter_masked_scan_uint32_peek (GstAdapter * adapter, guint32 mask, - guint32 pattern, gsize offset, gsize size, guint32 * value) -{ - gsize skip, bsize, i; - guint32 state; - GstMapInfo info; - guint8 *bdata; - GstBuffer *buf; - guint idx; - - g_return_val_if_fail (size > 0, -1); - g_return_val_if_fail (offset + size <= adapter->size, -1); - g_return_val_if_fail (((~mask) & pattern) == 0, -1); - - /* we can't find the pattern with less than 4 bytes */ - if (G_UNLIKELY (size < 4)) - return -1; - - skip = offset + adapter->skip; - - /* first step, do skipping and position on the first buffer */ - /* optimistically assume scanning continues sequentially */ - if (adapter->scan_entry_idx != G_MAXUINT && (adapter->scan_offset <= skip)) { - idx = adapter->scan_entry_idx; - skip -= adapter->scan_offset; - } else { - idx = 0; - adapter->scan_offset = 0; - adapter->scan_entry_idx = G_MAXUINT; - } - buf = gst_queue_array_peek_nth (adapter->bufqueue, idx++); - bsize = gst_buffer_get_size (buf); - while (G_UNLIKELY (skip >= bsize)) { - skip -= bsize; - adapter->scan_offset += bsize; - adapter->scan_entry_idx = idx; - buf = gst_queue_array_peek_nth (adapter->bufqueue, idx++); - bsize = gst_buffer_get_size (buf); - } - /* get the data now */ - if (!gst_buffer_map (buf, &info, GST_MAP_READ)) - return -1; - - bdata = (guint8 *) info.data + skip; - bsize = info.size - skip; - skip = 0; - - /* set the state to something that does not match */ - state = ~pattern; - - /* now find data */ - do { - bsize = MIN (bsize, size); - for (i = 0; i < bsize; i++) { - state = ((state << 8) | bdata[i]); - if (G_UNLIKELY ((state & mask) == pattern)) { - /* we have a match but we need to have skipped at - * least 4 bytes to fill the state. */ - if (G_LIKELY (skip + i >= 3)) { - if (G_LIKELY (value)) - *value = state; - gst_buffer_unmap (buf, &info); - return offset + skip + i - 3; - } - } - } - size -= bsize; - if (size == 0) - break; - - /* nothing found yet, go to next buffer */ - skip += bsize; - adapter->scan_offset += info.size; - adapter->scan_entry_idx = idx; - gst_buffer_unmap (buf, &info); - buf = gst_queue_array_peek_nth (adapter->bufqueue, idx++); - - if (!gst_buffer_map (buf, &info, GST_MAP_READ)) - return -1; - - bsize = info.size; - bdata = info.data; - } while (TRUE); - - gst_buffer_unmap (buf, &info); - - /* nothing found */ - return -1; -} - -/** - * gst_adapter_masked_scan_uint32: - * @adapter: a #GstAdapter - * @mask: mask to apply to data before matching against @pattern - * @pattern: pattern to match (after mask is applied) - * @offset: offset into the adapter data from which to start scanning, returns - * the last scanned position. - * @size: number of bytes to scan from offset - * - * Scan for pattern @pattern with applied mask @mask in the adapter data, - * starting from offset @offset. - * - * The bytes in @pattern and @mask are interpreted left-to-right, regardless - * of endianness. All four bytes of the pattern must be present in the - * adapter for it to match, even if the first or last bytes are masked out. - * - * It is an error to call this function without making sure that there is - * enough data (offset+size bytes) in the adapter. - * - * This function calls gst_adapter_masked_scan_uint32_peek() passing %NULL - * for value. - * - * Returns: offset of the first match, or -1 if no match was found. - * - * Example: - * |[ - * // Assume the adapter contains 0x00 0x01 0x02 ... 0xfe 0xff - * - * gst_adapter_masked_scan_uint32 (adapter, 0xffffffff, 0x00010203, 0, 256); - * // -> returns 0 - * gst_adapter_masked_scan_uint32 (adapter, 0xffffffff, 0x00010203, 1, 255); - * // -> returns -1 - * gst_adapter_masked_scan_uint32 (adapter, 0xffffffff, 0x01020304, 1, 255); - * // -> returns 1 - * gst_adapter_masked_scan_uint32 (adapter, 0xffff, 0x0001, 0, 256); - * // -> returns -1 - * gst_adapter_masked_scan_uint32 (adapter, 0xffff, 0x0203, 0, 256); - * // -> returns 0 - * gst_adapter_masked_scan_uint32 (adapter, 0xffff0000, 0x02030000, 0, 256); - * // -> returns 2 - * gst_adapter_masked_scan_uint32 (adapter, 0xffff0000, 0x02030000, 0, 4); - * // -> returns -1 - * ]| - */ -gssize -gst_adapter_masked_scan_uint32 (GstAdapter * adapter, guint32 mask, - guint32 pattern, gsize offset, gsize size) -{ - return gst_adapter_masked_scan_uint32_peek (adapter, mask, pattern, offset, - size, NULL); -} diff --git a/libs/gst/base/gstadapter.h b/libs/gst/base/gstadapter.h deleted file mode 100644 index 3945085a49..0000000000 --- a/libs/gst/base/gstadapter.h +++ /dev/null @@ -1,149 +0,0 @@ -/* GStreamer - * Copyright (C) 2004 Benjamin Otte <otte@gnome.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., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include <gst/gst.h> - -#ifndef __GST_ADAPTER_H__ -#define __GST_ADAPTER_H__ - -#include <gst/base/base-prelude.h> - -G_BEGIN_DECLS - - -#define GST_TYPE_ADAPTER \ - (gst_adapter_get_type()) -#define GST_ADAPTER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_ADAPTER, GstAdapter)) -#define GST_ADAPTER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_ADAPTER, GstAdapterClass)) -#define GST_ADAPTER_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_ADAPTER, GstAdapterClass)) -#define GST_IS_ADAPTER(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_ADAPTER)) -#define GST_IS_ADAPTER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_ADAPTER)) - -/** - * GstAdapter: - * - * The opaque #GstAdapter data structure. - */ -typedef struct _GstAdapter GstAdapter; -typedef struct _GstAdapterClass GstAdapterClass; - -GST_BASE_API -GType gst_adapter_get_type (void); - -GST_BASE_API -GstAdapter * gst_adapter_new (void) G_GNUC_MALLOC; - -GST_BASE_API -void gst_adapter_clear (GstAdapter *adapter); - -GST_BASE_API -void gst_adapter_push (GstAdapter *adapter, GstBuffer* buf); - -GST_BASE_API -gconstpointer gst_adapter_map (GstAdapter *adapter, gsize size); - -GST_BASE_API -void gst_adapter_unmap (GstAdapter *adapter); - -GST_BASE_API -void gst_adapter_copy (GstAdapter *adapter, gpointer dest, - gsize offset, gsize size); -GST_BASE_API -GBytes * gst_adapter_copy_bytes (GstAdapter *adapter, - gsize offset, gsize size); -GST_BASE_API -void gst_adapter_flush (GstAdapter *adapter, gsize flush); - -GST_BASE_API -gpointer gst_adapter_take (GstAdapter *adapter, gsize nbytes); - -GST_BASE_API -GstBuffer* gst_adapter_take_buffer (GstAdapter *adapter, gsize nbytes); - -GST_BASE_API -GList* gst_adapter_take_list (GstAdapter *adapter, gsize nbytes); - -GST_BASE_API -GstBuffer * gst_adapter_take_buffer_fast (GstAdapter *adapter, gsize nbytes); - -GST_BASE_API -GstBufferList * gst_adapter_take_buffer_list (GstAdapter *adapter, gsize nbytes); - -GST_BASE_API -GstBuffer* gst_adapter_get_buffer (GstAdapter *adapter, gsize nbytes); - -GST_BASE_API -GList* gst_adapter_get_list (GstAdapter *adapter, gsize nbytes); - -GST_BASE_API -GstBuffer * gst_adapter_get_buffer_fast (GstAdapter *adapter, gsize nbytes); - -GST_BASE_API -GstBufferList * gst_adapter_get_buffer_list (GstAdapter *adapter, gsize nbytes); - -GST_BASE_API -gsize gst_adapter_available (GstAdapter *adapter); - -GST_BASE_API -gsize gst_adapter_available_fast (GstAdapter *adapter); - -GST_BASE_API -GstClockTime gst_adapter_prev_pts (GstAdapter *adapter, guint64 *distance); - -GST_BASE_API -GstClockTime gst_adapter_prev_dts (GstAdapter *adapter, guint64 *distance); - -GST_BASE_API -GstClockTime gst_adapter_prev_pts_at_offset (GstAdapter * adapter, gsize offset, guint64 * distance); - -GST_BASE_API -GstClockTime gst_adapter_prev_dts_at_offset (GstAdapter * adapter, gsize offset, guint64 * distance); - -GST_BASE_API -guint64 gst_adapter_prev_offset (GstAdapter *adapter, guint64 *distance); - -GST_BASE_API -GstClockTime gst_adapter_pts_at_discont (GstAdapter *adapter); - -GST_BASE_API -GstClockTime gst_adapter_dts_at_discont (GstAdapter *adapter); - -GST_BASE_API -guint64 gst_adapter_offset_at_discont (GstAdapter *adapter); - -GST_BASE_API -guint64 gst_adapter_distance_from_discont (GstAdapter *adapter); - -GST_BASE_API -gssize gst_adapter_masked_scan_uint32 (GstAdapter * adapter, guint32 mask, - guint32 pattern, gsize offset, gsize size); -GST_BASE_API -gssize gst_adapter_masked_scan_uint32_peek (GstAdapter * adapter, guint32 mask, - guint32 pattern, gsize offset, gsize size, guint32 * value); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstAdapter, gst_object_unref) - -G_END_DECLS - -#endif /* __GST_ADAPTER_H__ */ diff --git a/libs/gst/base/gstaggregator.c b/libs/gst/base/gstaggregator.c deleted file mode 100644 index 5a6e30f42d..0000000000 --- a/libs/gst/base/gstaggregator.c +++ /dev/null @@ -1,3764 +0,0 @@ -/* GStreamer aggregator base class - * Copyright (C) 2014 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com> - * Copyright (C) 2014 Thibault Saunier <tsaunier@gnome.org> - * - * gstaggregator.c: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ -/** - * SECTION: gstaggregator - * @title: GstAggregator - * @short_description: Base class for mixers and muxers, manages a set of input - * pads and aggregates their streams - * @see_also: gstcollectpads for historical reasons. - * - * Manages a set of pads with the purpose of aggregating their buffers. - * Control is given to the subclass when all pads have data. - * - * * Base class for mixers and muxers. Subclasses should at least implement - * the #GstAggregatorClass::aggregate virtual method. - * - * * Installs a #GstPadChainFunction, a #GstPadEventFullFunction and a - * #GstPadQueryFunction to queue all serialized data packets per sink pad. - * Subclasses should not overwrite those, but instead implement - * #GstAggregatorClass::sink_event and #GstAggregatorClass::sink_query as - * needed. - * - * * When data is queued on all pads, the aggregate vmethod is called. - * - * * One can peek at the data on any given GstAggregatorPad with the - * gst_aggregator_pad_peek_buffer() method, and remove it from the pad - * with the gst_aggregator_pad_pop_buffer () method. When a buffer - * has been taken with pop_buffer (), a new buffer can be queued - * on that pad. - * - * * When gst_aggregator_pad_peek_buffer() or gst_aggregator_pad_has_buffer() - * are called, a reference is taken to the returned buffer, which stays - * valid until either: - * - * - gst_aggregator_pad_pop_buffer() is called, in which case the caller - * is guaranteed that the buffer they receive is the same as the peeked - * buffer. - * - gst_aggregator_pad_drop_buffer() is called, in which case the caller - * is guaranteed that the dropped buffer is the one that was peeked. - * - the subclass implementation of #GstAggregatorClass.aggregate returns. - * - * Subsequent calls to gst_aggregator_pad_peek_buffer() or - * gst_aggregator_pad_has_buffer() return / check the same buffer that was - * returned / checked, until one of the conditions listed above is met. - * - * Subclasses are only allowed to call these methods from the aggregate - * thread. - * - * * If the subclass wishes to push a buffer downstream in its aggregate - * implementation, it should do so through the - * gst_aggregator_finish_buffer() method. This method will take care - * of sending and ordering mandatory events such as stream start, caps - * and segment. Buffer lists can also be pushed out with - * gst_aggregator_finish_buffer_list(). - * - * * Same goes for EOS events, which should not be pushed directly by the - * subclass, it should instead return GST_FLOW_EOS in its aggregate - * implementation. - * - * * Note that the aggregator logic regarding gap event handling is to turn - * these into gap buffers with matching PTS and duration. It will also - * flag these buffers with GST_BUFFER_FLAG_GAP and GST_BUFFER_FLAG_DROPPABLE - * to ease their identification and subsequent processing. - * - * * Subclasses must use (a subclass of) #GstAggregatorPad for both their - * sink and source pads. - * See gst_element_class_add_static_pad_template_with_gtype(). - * - * This class used to live in gst-plugins-bad and was moved to core. - * - * Since: 1.14 - */ - -/** - * SECTION: gstaggregatorpad - * @title: GstAggregatorPad - * @short_description: #GstPad subclass for pads managed by #GstAggregator - * @see_also: gstcollectpads for historical reasons. - * - * Pads managed by a #GstAggregator subclass. - * - * This class used to live in gst-plugins-bad and was moved to core. - * - * Since: 1.14 - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include <string.h> /* strlen */ - -#include "gstaggregator.h" - -GType -gst_aggregator_start_time_selection_get_type (void) -{ - static GType gtype = 0; - - if (g_once_init_enter (>ype)) { - static const GEnumValue values[] = { - {GST_AGGREGATOR_START_TIME_SELECTION_ZERO, - "GST_AGGREGATOR_START_TIME_SELECTION_ZERO", "zero"}, - {GST_AGGREGATOR_START_TIME_SELECTION_FIRST, - "GST_AGGREGATOR_START_TIME_SELECTION_FIRST", "first"}, - {GST_AGGREGATOR_START_TIME_SELECTION_SET, - "GST_AGGREGATOR_START_TIME_SELECTION_SET", "set"}, - {0, NULL, NULL} - }; - GType new_type = - g_enum_register_static ("GstAggregatorStartTimeSelection", values); - - g_once_init_leave (>ype, new_type); - } - return gtype; -} - -/* Might become API */ -#if 0 -static void gst_aggregator_merge_tags (GstAggregator * aggregator, - const GstTagList * tags, GstTagMergeMode mode); -#endif -static void gst_aggregator_set_latency_property (GstAggregator * agg, - GstClockTime latency); -static GstClockTime gst_aggregator_get_latency_property (GstAggregator * agg); - -static GstClockTime gst_aggregator_get_latency_unlocked (GstAggregator * self); - -static void gst_aggregator_pad_buffer_consumed (GstAggregatorPad * pad, - GstBuffer * buffer, gboolean dequeued); - -GST_DEBUG_CATEGORY_STATIC (aggregator_debug); -#define GST_CAT_DEFAULT aggregator_debug - -/* Locking order, locks in this element must always be taken in this order - * - * standard sink pad stream lock -> GST_PAD_STREAM_LOCK (aggpad) - * Aggregator pad flush lock -> PAD_FLUSH_LOCK(aggpad) - * standard src pad stream lock -> GST_PAD_STREAM_LOCK (srcpad) - * Aggregator src lock -> SRC_LOCK(agg) w/ SRC_WAIT/BROADCAST - * standard element object lock -> GST_OBJECT_LOCK(agg) - * Aggregator pad lock -> PAD_LOCK (aggpad) w/ PAD_WAIT/BROADCAST_EVENT(aggpad) - * standard src pad object lock -> GST_OBJECT_LOCK(srcpad) - * standard sink pad object lock -> GST_OBJECT_LOCK(aggpad) - */ - -/* GstAggregatorPad definitions */ -#define PAD_LOCK(pad) G_STMT_START { \ - GST_TRACE_OBJECT (pad, "Taking PAD lock from thread %p", \ - g_thread_self()); \ - g_mutex_lock(&pad->priv->lock); \ - GST_TRACE_OBJECT (pad, "Took PAD lock from thread %p", \ - g_thread_self()); \ - } G_STMT_END - -#define PAD_UNLOCK(pad) G_STMT_START { \ - GST_TRACE_OBJECT (pad, "Releasing PAD lock from thread %p", \ - g_thread_self()); \ - g_mutex_unlock(&pad->priv->lock); \ - GST_TRACE_OBJECT (pad, "Release PAD lock from thread %p", \ - g_thread_self()); \ - } G_STMT_END - - -#define PAD_WAIT_EVENT(pad) G_STMT_START { \ - GST_LOG_OBJECT (pad, "Waiting for buffer to be consumed thread %p", \ - g_thread_self()); \ - g_cond_wait(&(((GstAggregatorPad* )pad)->priv->event_cond), \ - (&((GstAggregatorPad*)pad)->priv->lock)); \ - GST_LOG_OBJECT (pad, "DONE Waiting for buffer to be consumed on thread %p", \ - g_thread_self()); \ - } G_STMT_END - -#define PAD_BROADCAST_EVENT(pad) G_STMT_START { \ - GST_LOG_OBJECT (pad, "Signaling buffer consumed from thread %p", \ - g_thread_self()); \ - g_cond_broadcast(&(((GstAggregatorPad* )pad)->priv->event_cond)); \ - } G_STMT_END - - -#define PAD_FLUSH_LOCK(pad) G_STMT_START { \ - GST_TRACE_OBJECT (pad, "Taking lock from thread %p", \ - g_thread_self()); \ - g_mutex_lock(&pad->priv->flush_lock); \ - GST_TRACE_OBJECT (pad, "Took lock from thread %p", \ - g_thread_self()); \ - } G_STMT_END - -#define PAD_FLUSH_UNLOCK(pad) G_STMT_START { \ - GST_TRACE_OBJECT (pad, "Releasing lock from thread %p", \ - g_thread_self()); \ - g_mutex_unlock(&pad->priv->flush_lock); \ - GST_TRACE_OBJECT (pad, "Release lock from thread %p", \ - g_thread_self()); \ - } G_STMT_END - -#define SRC_LOCK(self) G_STMT_START { \ - GST_TRACE_OBJECT (self, "Taking src lock from thread %p", \ - g_thread_self()); \ - g_mutex_lock(&self->priv->src_lock); \ - GST_TRACE_OBJECT (self, "Took src lock from thread %p", \ - g_thread_self()); \ - } G_STMT_END - -#define SRC_UNLOCK(self) G_STMT_START { \ - GST_TRACE_OBJECT (self, "Releasing src lock from thread %p", \ - g_thread_self()); \ - g_mutex_unlock(&self->priv->src_lock); \ - GST_TRACE_OBJECT (self, "Released src lock from thread %p", \ - g_thread_self()); \ - } G_STMT_END - -#define SRC_WAIT(self) G_STMT_START { \ - GST_LOG_OBJECT (self, "Waiting for src on thread %p", \ - g_thread_self()); \ - g_cond_wait(&(self->priv->src_cond), &(self->priv->src_lock)); \ - GST_LOG_OBJECT (self, "DONE Waiting for src on thread %p", \ - g_thread_self()); \ - } G_STMT_END - -#define SRC_BROADCAST(self) G_STMT_START { \ - GST_LOG_OBJECT (self, "Signaling src from thread %p", \ - g_thread_self()); \ - if (self->priv->aggregate_id) \ - gst_clock_id_unschedule (self->priv->aggregate_id); \ - g_cond_broadcast(&(self->priv->src_cond)); \ - } G_STMT_END - -struct _GstAggregatorPadPrivate -{ - /* Following fields are protected by the PAD_LOCK */ - GstFlowReturn flow_return; - - guint32 last_flush_start_seqnum; - guint32 last_flush_stop_seqnum; - - gboolean first_buffer; - - GQueue data; /* buffers, events and queries */ - GstBuffer *clipped_buffer; - guint num_buffers; - GstBuffer *peeked_buffer; - - /* used to track fill state of queues, only used with live-src and when - * latency property is set to > 0 */ - GstClockTime head_position; - GstClockTime tail_position; - GstClockTime head_time; /* running time */ - GstClockTime tail_time; - GstClockTime time_level; /* how much head is ahead of tail */ - GstSegment head_segment; /* segment before the queue */ - - gboolean negotiated; - - gboolean eos; - - GMutex lock; - GCond event_cond; - /* This lock prevents a flush start processing happening while - * the chain function is also happening. - */ - GMutex flush_lock; - - /* properties */ - gboolean emit_signals; -}; - -/* Must be called with PAD_LOCK held */ -static void -gst_aggregator_pad_reset_unlocked (GstAggregatorPad * aggpad) -{ - aggpad->priv->eos = FALSE; - aggpad->priv->flow_return = GST_FLOW_OK; - GST_OBJECT_LOCK (aggpad); - gst_segment_init (&aggpad->segment, GST_FORMAT_UNDEFINED); - gst_segment_init (&aggpad->priv->head_segment, GST_FORMAT_UNDEFINED); - GST_OBJECT_UNLOCK (aggpad); - aggpad->priv->head_position = GST_CLOCK_TIME_NONE; - aggpad->priv->tail_position = GST_CLOCK_TIME_NONE; - aggpad->priv->head_time = GST_CLOCK_TIME_NONE; - aggpad->priv->tail_time = GST_CLOCK_TIME_NONE; - aggpad->priv->time_level = 0; - aggpad->priv->first_buffer = TRUE; -} - -static gboolean -gst_aggregator_pad_flush (GstAggregatorPad * aggpad, GstAggregator * agg) -{ - GstAggregatorPadClass *klass = GST_AGGREGATOR_PAD_GET_CLASS (aggpad); - - PAD_LOCK (aggpad); - gst_aggregator_pad_reset_unlocked (aggpad); - PAD_UNLOCK (aggpad); - - if (klass->flush) - return (klass->flush (aggpad, agg) == GST_FLOW_OK); - - return TRUE; -} - -/** - * gst_aggregator_peek_next_sample: - * - * Use this function to determine what input buffers will be aggregated - * to produce the next output buffer. This should only be called from - * a #GstAggregator::samples-selected handler, and can be used to precisely - * control aggregating parameters for a given set of input samples. - * - * Returns: (nullable) (transfer full): The sample that is about to be aggregated. It may hold a #GstBuffer - * or a #GstBufferList. The contents of its info structure is subclass-dependent, - * and documented on a subclass basis. The buffers held by the sample are - * not writable. - * Since: 1.18 - */ -GstSample * -gst_aggregator_peek_next_sample (GstAggregator * agg, GstAggregatorPad * aggpad) -{ - GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (agg); - - if (klass->peek_next_sample) - return (klass->peek_next_sample (agg, aggpad)); - - return NULL; -} - -/************************************* - * GstAggregator implementation * - *************************************/ -static GstElementClass *aggregator_parent_class = NULL; -static gint aggregator_private_offset = 0; - -/* All members are protected by the object lock unless otherwise noted */ - -struct _GstAggregatorPrivate -{ - gint max_padserial; - - /* Our state is >= PAUSED */ - gboolean running; /* protected by src_lock */ - - /* seqnum from last seek or common seqnum to flush start events received - * on all pads, for flushing without a seek */ - guint32 next_seqnum; - /* seqnum to apply to synthetic segment/eos events */ - guint32 seqnum; - gboolean send_stream_start; /* protected by srcpad stream lock */ - gboolean send_segment; - gboolean flushing; - gboolean send_eos; /* protected by srcpad stream lock */ - - GstCaps *srccaps; /* protected by the srcpad stream lock */ - - GstTagList *tags; - gboolean tags_changed; - - gboolean peer_latency_live; /* protected by src_lock */ - GstClockTime peer_latency_min; /* protected by src_lock */ - GstClockTime peer_latency_max; /* protected by src_lock */ - gboolean has_peer_latency; /* protected by src_lock */ - - GstClockTime sub_latency_min; /* protected by src_lock */ - GstClockTime sub_latency_max; /* protected by src_lock */ - - GstClockTime upstream_latency_min; /* protected by src_lock */ - - /* aggregate */ - GstClockID aggregate_id; /* protected by src_lock */ - gboolean selected_samples_called_or_warned; /* protected by src_lock */ - GMutex src_lock; - GCond src_cond; - - gboolean first_buffer; /* protected by object lock */ - GstAggregatorStartTimeSelection start_time_selection; - GstClockTime start_time; - - /* protected by the object lock */ - GstQuery *allocation_query; - GstAllocator *allocator; - GstBufferPool *pool; - GstAllocationParams allocation_params; - - /* properties */ - gint64 latency; /* protected by both src_lock and all pad locks */ - gboolean emit_signals; -}; - -/* Seek event forwarding helper */ -typedef struct -{ - /* parameters */ - GstEvent *event; - gboolean flush; - gboolean only_to_active_pads; - - /* results */ - gboolean result; - gboolean one_actually_seeked; -} EventData; - -#define DEFAULT_LATENCY 0 -#define DEFAULT_MIN_UPSTREAM_LATENCY 0 -#define DEFAULT_START_TIME_SELECTION GST_AGGREGATOR_START_TIME_SELECTION_ZERO -#define DEFAULT_START_TIME (-1) -#define DEFAULT_EMIT_SIGNALS FALSE - -enum -{ - PROP_0, - PROP_LATENCY, - PROP_MIN_UPSTREAM_LATENCY, - PROP_START_TIME_SELECTION, - PROP_START_TIME, - PROP_EMIT_SIGNALS, - PROP_LAST -}; - -enum -{ - SIGNAL_SAMPLES_SELECTED, - LAST_SIGNAL, -}; - -static guint gst_aggregator_signals[LAST_SIGNAL] = { 0 }; - -static GstFlowReturn gst_aggregator_pad_chain_internal (GstAggregator * self, - GstAggregatorPad * aggpad, GstBuffer * buffer, gboolean head); - -static gboolean -gst_aggregator_pad_queue_is_empty (GstAggregatorPad * pad) -{ - return (g_queue_peek_tail (&pad->priv->data) == NULL && - pad->priv->clipped_buffer == NULL); -} - -/* Will return FALSE if there's no buffer available on every non-EOS pad, or - * if at least one of the pads has an event or query at the top of its queue. - * - * Only returns TRUE if all non-EOS pads have a buffer available at the top of - * their queue or a clipped buffer already. - */ -static gboolean -gst_aggregator_check_pads_ready (GstAggregator * self, - gboolean * have_event_or_query_ret) -{ - GstAggregatorPad *pad = NULL; - GList *l, *sinkpads; - gboolean have_buffer = TRUE; - gboolean have_event_or_query = FALSE; - - GST_LOG_OBJECT (self, "checking pads"); - - GST_OBJECT_LOCK (self); - - sinkpads = GST_ELEMENT_CAST (self)->sinkpads; - if (sinkpads == NULL) - goto no_sinkpads; - - for (l = sinkpads; l != NULL; l = l->next) { - pad = l->data; - - PAD_LOCK (pad); - - /* If there's an event or query at the top of the queue and we don't yet - * have taken the top buffer out and stored it as clip_buffer, remember - * that and exit the loop. We first have to handle all events/queries - * before we handle any buffers. */ - if (!pad->priv->clipped_buffer - && (GST_IS_EVENT (g_queue_peek_tail (&pad->priv->data)) - || GST_IS_QUERY (g_queue_peek_tail (&pad->priv->data)))) { - PAD_UNLOCK (pad); - have_event_or_query = TRUE; - break; - } - - /* Otherwise check if we have a clipped buffer or a buffer at the top of - * the queue, and if not then this pad is not ready unless it is also EOS */ - if (!pad->priv->clipped_buffer - && !GST_IS_BUFFER (g_queue_peek_tail (&pad->priv->data))) { - /* We must not have any buffers at all in this pad then as otherwise we - * would've had an event/query at the top of the queue */ - g_assert (pad->priv->num_buffers == 0); - - /* Only consider this pad as worth waiting for if it's not already EOS. - * There's no point in waiting for buffers on EOS pads */ - if (!pad->priv->eos) - have_buffer = FALSE; - } else if (self->priv->peer_latency_live) { - /* In live mode, having a single pad with buffers is enough to - * generate a start time from it. In non-live mode all pads need - * to have a buffer - */ - self->priv->first_buffer = FALSE; - } - - PAD_UNLOCK (pad); - } - - if (have_event_or_query) - goto pad_not_ready_but_event_or_query; - - if (!have_buffer) - goto pad_not_ready; - - if (have_buffer) - self->priv->first_buffer = FALSE; - - GST_OBJECT_UNLOCK (self); - GST_LOG_OBJECT (self, "pads are ready"); - - if (have_event_or_query_ret) - *have_event_or_query_ret = have_event_or_query; - - return TRUE; - -no_sinkpads: - { - GST_LOG_OBJECT (self, "pads not ready: no sink pads"); - GST_OBJECT_UNLOCK (self); - - if (have_event_or_query_ret) - *have_event_or_query_ret = have_event_or_query; - - return FALSE; - } -pad_not_ready: - { - GST_LOG_OBJECT (pad, "pad not ready to be aggregated yet"); - GST_OBJECT_UNLOCK (self); - - if (have_event_or_query_ret) - *have_event_or_query_ret = have_event_or_query; - - return FALSE; - } -pad_not_ready_but_event_or_query: - { - GST_LOG_OBJECT (pad, - "pad not ready to be aggregated yet, need to handle serialized event or query first"); - GST_OBJECT_UNLOCK (self); - - if (have_event_or_query_ret) - *have_event_or_query_ret = have_event_or_query; - - return FALSE; - } -} - -static void -gst_aggregator_reset_flow_values (GstAggregator * self) -{ - GST_OBJECT_LOCK (self); - self->priv->send_stream_start = TRUE; - self->priv->send_segment = TRUE; - gst_segment_init (&GST_AGGREGATOR_PAD (self->srcpad)->segment, - GST_FORMAT_TIME); - /* Initialize to -1 so we set it to the start position once the first buffer - * is handled in gst_aggregator_pad_chain_internal() */ - GST_AGGREGATOR_PAD (self->srcpad)->segment.position = -1; - self->priv->first_buffer = TRUE; - GST_OBJECT_UNLOCK (self); -} - -static inline void -gst_aggregator_push_mandatory_events (GstAggregator * self) -{ - GstAggregatorPrivate *priv = self->priv; - GstEvent *segment = NULL; - GstEvent *tags = NULL; - - if (self->priv->send_stream_start) { - gchar s_id[32]; - - GST_INFO_OBJECT (self, "pushing stream start"); - /* stream-start (FIXME: create id based on input ids) */ - g_snprintf (s_id, sizeof (s_id), "agg-%08x", g_random_int ()); - if (!gst_pad_push_event (GST_PAD (self->srcpad), - gst_event_new_stream_start (s_id))) { - GST_WARNING_OBJECT (self->srcpad, "Sending stream start event failed"); - } - self->priv->send_stream_start = FALSE; - } - - if (self->priv->srccaps) { - - GST_INFO_OBJECT (self, "pushing caps: %" GST_PTR_FORMAT, - self->priv->srccaps); - if (!gst_pad_push_event (GST_PAD (self->srcpad), - gst_event_new_caps (self->priv->srccaps))) { - GST_WARNING_OBJECT (self->srcpad, "Sending caps event failed"); - } - gst_caps_unref (self->priv->srccaps); - self->priv->srccaps = NULL; - } - - GST_OBJECT_LOCK (self); - if (self->priv->send_segment && !self->priv->flushing) { - segment = - gst_event_new_segment (&GST_AGGREGATOR_PAD (self->srcpad)->segment); - - if (!self->priv->seqnum) - /* This code-path is in preparation to be able to run without a source - * connected. Then we won't have a seq-num from a segment event. */ - self->priv->seqnum = gst_event_get_seqnum (segment); - else - gst_event_set_seqnum (segment, self->priv->seqnum); - self->priv->send_segment = FALSE; - - GST_DEBUG_OBJECT (self, "pushing segment %" GST_PTR_FORMAT, segment); - } - - if (priv->tags && priv->tags_changed && !self->priv->flushing) { - tags = gst_event_new_tag (gst_tag_list_ref (priv->tags)); - priv->tags_changed = FALSE; - } - GST_OBJECT_UNLOCK (self); - - if (segment) - gst_pad_push_event (self->srcpad, segment); - if (tags) - gst_pad_push_event (self->srcpad, tags); - -} - -/** - * gst_aggregator_set_src_caps: - * @self: The #GstAggregator - * @caps: The #GstCaps to set on the src pad. - * - * Sets the caps to be used on the src pad. - */ -void -gst_aggregator_set_src_caps (GstAggregator * self, GstCaps * caps) -{ - GST_PAD_STREAM_LOCK (self->srcpad); - gst_caps_replace (&self->priv->srccaps, caps); - gst_aggregator_push_mandatory_events (self); - GST_PAD_STREAM_UNLOCK (self->srcpad); -} - -static GstFlowReturn -gst_aggregator_default_finish_buffer (GstAggregator * self, GstBuffer * buffer) -{ - gst_aggregator_push_mandatory_events (self); - - GST_OBJECT_LOCK (self); - if (!self->priv->flushing && gst_pad_is_active (self->srcpad)) { - GST_TRACE_OBJECT (self, "pushing buffer %" GST_PTR_FORMAT, buffer); - GST_OBJECT_UNLOCK (self); - return gst_pad_push (self->srcpad, buffer); - } else { - GST_INFO_OBJECT (self, "Not pushing (active: %i, flushing: %i)", - self->priv->flushing, gst_pad_is_active (self->srcpad)); - GST_OBJECT_UNLOCK (self); - gst_buffer_unref (buffer); - return GST_FLOW_OK; - } -} - -/** - * gst_aggregator_finish_buffer: - * @aggregator: The #GstAggregator - * @buffer: (transfer full): the #GstBuffer to push. - * - * This method will push the provided output buffer downstream. If needed, - * mandatory events such as stream-start, caps, and segment events will be - * sent before pushing the buffer. - */ -GstFlowReturn -gst_aggregator_finish_buffer (GstAggregator * aggregator, GstBuffer * buffer) -{ - GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (aggregator); - - g_assert (klass->finish_buffer != NULL); - - return klass->finish_buffer (aggregator, buffer); -} - -static GstFlowReturn -gst_aggregator_default_finish_buffer_list (GstAggregator * self, - GstBufferList * bufferlist) -{ - gst_aggregator_push_mandatory_events (self); - - GST_OBJECT_LOCK (self); - if (!self->priv->flushing && gst_pad_is_active (self->srcpad)) { - GST_TRACE_OBJECT (self, "pushing bufferlist%" GST_PTR_FORMAT, bufferlist); - GST_OBJECT_UNLOCK (self); - return gst_pad_push_list (self->srcpad, bufferlist); - } else { - GST_INFO_OBJECT (self, "Not pushing (active: %i, flushing: %i)", - self->priv->flushing, gst_pad_is_active (self->srcpad)); - GST_OBJECT_UNLOCK (self); - gst_buffer_list_unref (bufferlist); - return GST_FLOW_OK; - } -} - -/** - * gst_aggregator_finish_buffer_list: - * @aggregator: The #GstAggregator - * @bufferlist: (transfer full): the #GstBufferList to push. - * - * This method will push the provided output buffer list downstream. If needed, - * mandatory events such as stream-start, caps, and segment events will be - * sent before pushing the buffer. - * - * Since: 1.18 - */ -GstFlowReturn -gst_aggregator_finish_buffer_list (GstAggregator * aggregator, - GstBufferList * bufferlist) -{ - GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (aggregator); - - g_assert (klass->finish_buffer_list != NULL); - - return klass->finish_buffer_list (aggregator, bufferlist); -} - -static void -gst_aggregator_push_eos (GstAggregator * self) -{ - GstEvent *event; - gst_aggregator_push_mandatory_events (self); - - event = gst_event_new_eos (); - - GST_OBJECT_LOCK (self); - self->priv->send_eos = FALSE; - gst_event_set_seqnum (event, self->priv->seqnum); - GST_OBJECT_UNLOCK (self); - - gst_pad_push_event (self->srcpad, event); -} - -static GstClockTime -gst_aggregator_get_next_time (GstAggregator * self) -{ - GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (self); - - if (klass->get_next_time) - return klass->get_next_time (self); - - return GST_CLOCK_TIME_NONE; -} - -static gboolean -gst_aggregator_wait_and_check (GstAggregator * self, gboolean * timeout) -{ - GstClockTime latency; - GstClockTime start; - gboolean res; - gboolean have_event_or_query = FALSE; - - *timeout = FALSE; - - SRC_LOCK (self); - - latency = gst_aggregator_get_latency_unlocked (self); - - if (gst_aggregator_check_pads_ready (self, &have_event_or_query)) { - GST_DEBUG_OBJECT (self, "all pads have data"); - SRC_UNLOCK (self); - - return TRUE; - } - - /* If we have an event or query, immediately return FALSE instead of waiting - * and handle it immediately */ - if (have_event_or_query) { - GST_DEBUG_OBJECT (self, "Have serialized event or query to handle first"); - SRC_UNLOCK (self); - return FALSE; - } - - /* Before waiting, check if we're actually still running */ - if (!self->priv->running || !self->priv->send_eos) { - SRC_UNLOCK (self); - - return FALSE; - } - - start = gst_aggregator_get_next_time (self); - - /* If we're not live, or if we use the running time - * of the first buffer as start time, we wait until - * all pads have buffers. - * Otherwise (i.e. if we are live!), we wait on the clock - * and if a pad does not have a buffer in time we ignore - * that pad. - */ - GST_OBJECT_LOCK (self); - if (!GST_CLOCK_TIME_IS_VALID (latency) || - !GST_IS_CLOCK (GST_ELEMENT_CLOCK (self)) || - !GST_CLOCK_TIME_IS_VALID (start) || - (self->priv->first_buffer - && self->priv->start_time_selection == - GST_AGGREGATOR_START_TIME_SELECTION_FIRST)) { - /* We wake up here when something happened, and below - * then check if we're ready now. If we return FALSE, - * we will be directly called again. - */ - GST_OBJECT_UNLOCK (self); - SRC_WAIT (self); - } else { - GstClockTime base_time, time; - GstClock *clock; - GstClockReturn status; - GstClockTimeDiff jitter; - - GST_DEBUG_OBJECT (self, "got subclass start time: %" GST_TIME_FORMAT, - GST_TIME_ARGS (start)); - - base_time = GST_ELEMENT_CAST (self)->base_time; - clock = gst_object_ref (GST_ELEMENT_CLOCK (self)); - GST_OBJECT_UNLOCK (self); - - time = base_time + start; - time += latency; - - GST_DEBUG_OBJECT (self, "possibly waiting for clock to reach %" - GST_TIME_FORMAT " (base %" GST_TIME_FORMAT " start %" GST_TIME_FORMAT - " latency %" GST_TIME_FORMAT " current %" GST_TIME_FORMAT ")", - GST_TIME_ARGS (time), - GST_TIME_ARGS (base_time), - GST_TIME_ARGS (start), GST_TIME_ARGS (latency), - GST_TIME_ARGS (gst_clock_get_time (clock))); - - self->priv->aggregate_id = gst_clock_new_single_shot_id (clock, time); - gst_object_unref (clock); - SRC_UNLOCK (self); - - jitter = 0; - status = gst_clock_id_wait (self->priv->aggregate_id, &jitter); - - SRC_LOCK (self); - if (self->priv->aggregate_id) { - gst_clock_id_unref (self->priv->aggregate_id); - self->priv->aggregate_id = NULL; - } - - GST_DEBUG_OBJECT (self, - "clock returned %d (jitter: %" GST_STIME_FORMAT ")", - status, GST_STIME_ARGS (jitter)); - - /* we timed out */ - if (status == GST_CLOCK_OK || status == GST_CLOCK_EARLY) { - SRC_UNLOCK (self); - *timeout = TRUE; - return TRUE; - } - } - - res = gst_aggregator_check_pads_ready (self, NULL); - SRC_UNLOCK (self); - - return res; -} - -typedef struct -{ - gboolean processed_event; - GstFlowReturn flow_ret; -} DoHandleEventsAndQueriesData; - -static gboolean -gst_aggregator_do_events_and_queries (GstElement * self, GstPad * epad, - gpointer user_data) -{ - GstAggregatorPad *pad = GST_AGGREGATOR_PAD_CAST (epad); - GstAggregator *aggregator = GST_AGGREGATOR_CAST (self); - GstEvent *event = NULL; - GstQuery *query = NULL; - GstAggregatorClass *klass = NULL; - DoHandleEventsAndQueriesData *data = user_data; - - do { - event = NULL; - query = NULL; - - PAD_LOCK (pad); - if (pad->priv->clipped_buffer == NULL && - !GST_IS_BUFFER (g_queue_peek_tail (&pad->priv->data))) { - if (GST_IS_EVENT (g_queue_peek_tail (&pad->priv->data))) - event = gst_event_ref (g_queue_peek_tail (&pad->priv->data)); - if (GST_IS_QUERY (g_queue_peek_tail (&pad->priv->data))) - query = g_queue_peek_tail (&pad->priv->data); - } - PAD_UNLOCK (pad); - if (event || query) { - gboolean ret; - - data->processed_event = TRUE; - if (klass == NULL) - klass = GST_AGGREGATOR_GET_CLASS (self); - - if (event) { - GST_LOG_OBJECT (pad, "Processing %" GST_PTR_FORMAT, event); - gst_event_ref (event); - ret = klass->sink_event (aggregator, pad, event); - - PAD_LOCK (pad); - if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) { - pad->priv->negotiated = ret; - } - if (g_queue_peek_tail (&pad->priv->data) == event) - gst_event_unref (g_queue_pop_tail (&pad->priv->data)); - gst_event_unref (event); - } else if (query) { - GST_LOG_OBJECT (pad, "Processing %" GST_PTR_FORMAT, query); - ret = klass->sink_query (aggregator, pad, query); - - PAD_LOCK (pad); - if (g_queue_peek_tail (&pad->priv->data) == query) { - GstStructure *s; - - s = gst_query_writable_structure (query); - gst_structure_set (s, "gst-aggregator-retval", G_TYPE_BOOLEAN, ret, - NULL); - g_queue_pop_tail (&pad->priv->data); - } - } - - PAD_BROADCAST_EVENT (pad); - PAD_UNLOCK (pad); - } - } while (event || query); - - return TRUE; -} - -static gboolean -gst_aggregator_pad_skip_buffers (GstElement * self, GstPad * epad, - gpointer user_data) -{ - GList *item; - GstAggregatorPad *aggpad = (GstAggregatorPad *) epad; - GstAggregator *agg = (GstAggregator *) self; - GstAggregatorPadClass *klass = GST_AGGREGATOR_PAD_GET_CLASS (aggpad); - - if (!klass->skip_buffer) - return FALSE; - - PAD_LOCK (aggpad); - - item = g_queue_peek_tail_link (&aggpad->priv->data); - while (item) { - GList *prev = item->prev; - - if (GST_IS_BUFFER (item->data) - && klass->skip_buffer (aggpad, agg, item->data)) { - GST_LOG_OBJECT (aggpad, "Skipping %" GST_PTR_FORMAT, item->data); - gst_aggregator_pad_buffer_consumed (aggpad, GST_BUFFER (item->data), - TRUE); - gst_buffer_unref (item->data); - g_queue_delete_link (&aggpad->priv->data, item); - } else { - break; - } - - item = prev; - } - - PAD_UNLOCK (aggpad); - - return TRUE; -} - -static gboolean -gst_aggregator_pad_reset_peeked_buffer (GstElement * self, GstPad * epad, - gpointer user_data) -{ - GstAggregatorPad *aggpad = (GstAggregatorPad *) epad; - - PAD_LOCK (aggpad); - - gst_buffer_replace (&aggpad->priv->peeked_buffer, NULL); - - PAD_UNLOCK (aggpad); - - return TRUE; -} - - -static void -gst_aggregator_pad_set_flushing (GstAggregatorPad * aggpad, - GstFlowReturn flow_return, gboolean full) -{ - GList *item; - - PAD_LOCK (aggpad); - if (flow_return == GST_FLOW_NOT_LINKED) - aggpad->priv->flow_return = MIN (flow_return, aggpad->priv->flow_return); - else - aggpad->priv->flow_return = flow_return; - - item = g_queue_peek_head_link (&aggpad->priv->data); - while (item) { - GList *next = item->next; - - /* In partial flush, we do like the pad, we get rid of non-sticky events - * and EOS/SEGMENT. - */ - if (full || GST_IS_BUFFER (item->data) || - GST_EVENT_TYPE (item->data) == GST_EVENT_EOS || - GST_EVENT_TYPE (item->data) == GST_EVENT_SEGMENT || - !GST_EVENT_IS_STICKY (item->data)) { - if (!GST_IS_QUERY (item->data)) - gst_mini_object_unref (item->data); - g_queue_delete_link (&aggpad->priv->data, item); - } - item = next; - } - aggpad->priv->num_buffers = 0; - gst_buffer_replace (&aggpad->priv->clipped_buffer, NULL); - - PAD_BROADCAST_EVENT (aggpad); - PAD_UNLOCK (aggpad); -} - -static GstFlowReturn -gst_aggregator_default_update_src_caps (GstAggregator * agg, GstCaps * caps, - GstCaps ** ret) -{ - *ret = gst_caps_ref (caps); - - return GST_FLOW_OK; -} - -static GstCaps * -gst_aggregator_default_fixate_src_caps (GstAggregator * agg, GstCaps * caps) -{ - caps = gst_caps_fixate (caps); - - return caps; -} - -static gboolean -gst_aggregator_default_negotiated_src_caps (GstAggregator * agg, GstCaps * caps) -{ - return TRUE; -} - - -/* takes ownership of the pool, allocator and query */ -static gboolean -gst_aggregator_set_allocation (GstAggregator * self, - GstBufferPool * pool, GstAllocator * allocator, - const GstAllocationParams * params, GstQuery * query) -{ - GstAllocator *oldalloc; - GstBufferPool *oldpool; - GstQuery *oldquery; - - GST_DEBUG ("storing allocation query"); - - GST_OBJECT_LOCK (self); - oldpool = self->priv->pool; - self->priv->pool = pool; - - oldalloc = self->priv->allocator; - self->priv->allocator = allocator; - - oldquery = self->priv->allocation_query; - self->priv->allocation_query = query; - - if (params) - self->priv->allocation_params = *params; - else - gst_allocation_params_init (&self->priv->allocation_params); - GST_OBJECT_UNLOCK (self); - - if (oldpool) { - GST_DEBUG_OBJECT (self, "deactivating old pool %p", oldpool); - gst_buffer_pool_set_active (oldpool, FALSE); - gst_object_unref (oldpool); - } - if (oldalloc) { - gst_object_unref (oldalloc); - } - if (oldquery) { - gst_query_unref (oldquery); - } - return TRUE; -} - - -static gboolean -gst_aggregator_decide_allocation (GstAggregator * self, GstQuery * query) -{ - GstAggregatorClass *aggclass = GST_AGGREGATOR_GET_CLASS (self); - - if (aggclass->decide_allocation) - if (!aggclass->decide_allocation (self, query)) - return FALSE; - - return TRUE; -} - -static gboolean -gst_aggregator_do_allocation (GstAggregator * self, GstCaps * caps) -{ - GstQuery *query; - gboolean result = TRUE; - GstBufferPool *pool = NULL; - GstAllocator *allocator; - GstAllocationParams params; - - /* find a pool for the negotiated caps now */ - GST_DEBUG_OBJECT (self, "doing allocation query"); - query = gst_query_new_allocation (caps, TRUE); - if (!gst_pad_peer_query (self->srcpad, query)) { - /* not a problem, just debug a little */ - GST_DEBUG_OBJECT (self, "peer ALLOCATION query failed"); - } - - GST_DEBUG_OBJECT (self, "calling decide_allocation"); - result = gst_aggregator_decide_allocation (self, query); - - GST_DEBUG_OBJECT (self, "ALLOCATION (%d) params: %" GST_PTR_FORMAT, result, - query); - - if (!result) - goto no_decide_allocation; - - /* we got configuration from our peer or the decide_allocation method, - * parse them */ - if (gst_query_get_n_allocation_params (query) > 0) { - gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶ms); - } else { - allocator = NULL; - gst_allocation_params_init (¶ms); - } - - if (gst_query_get_n_allocation_pools (query) > 0) - gst_query_parse_nth_allocation_pool (query, 0, &pool, NULL, NULL, NULL); - - /* now store */ - result = - gst_aggregator_set_allocation (self, pool, allocator, ¶ms, query); - - return result; - - /* Errors */ -no_decide_allocation: - { - GST_WARNING_OBJECT (self, "Failed to decide allocation"); - gst_query_unref (query); - - return result; - } - -} - -static gboolean -gst_aggregator_default_negotiate (GstAggregator * self) -{ - GstAggregatorClass *agg_klass = GST_AGGREGATOR_GET_CLASS (self); - GstCaps *downstream_caps, *template_caps, *caps = NULL; - GstFlowReturn ret = GST_FLOW_OK; - - template_caps = gst_pad_get_pad_template_caps (self->srcpad); - downstream_caps = gst_pad_peer_query_caps (self->srcpad, template_caps); - - if (gst_caps_is_empty (downstream_caps)) { - GST_INFO_OBJECT (self, "Downstream caps (%" - GST_PTR_FORMAT ") not compatible with pad template caps (%" - GST_PTR_FORMAT ")", downstream_caps, template_caps); - ret = GST_FLOW_NOT_NEGOTIATED; - goto done; - } - - g_assert (agg_klass->update_src_caps); - GST_DEBUG_OBJECT (self, "updating caps from %" GST_PTR_FORMAT, - downstream_caps); - ret = agg_klass->update_src_caps (self, downstream_caps, &caps); - if (ret < GST_FLOW_OK) { - GST_WARNING_OBJECT (self, "Subclass failed to update provided caps"); - goto done; - } else if (ret == GST_AGGREGATOR_FLOW_NEED_DATA) { - GST_DEBUG_OBJECT (self, "Subclass needs more data to decide on caps"); - goto done; - } - if ((caps == NULL || gst_caps_is_empty (caps)) && ret >= GST_FLOW_OK) { - ret = GST_FLOW_NOT_NEGOTIATED; - goto done; - } - GST_DEBUG_OBJECT (self, " to %" GST_PTR_FORMAT, caps); - -#ifdef GST_ENABLE_EXTRA_CHECKS - if (!gst_caps_is_subset (caps, template_caps)) { - GstCaps *intersection; - - GST_ERROR_OBJECT (self, - "update_src_caps returned caps %" GST_PTR_FORMAT - " which are not a real subset of the template caps %" - GST_PTR_FORMAT, caps, template_caps); - g_warning ("%s: update_src_caps returned caps which are not a real " - "subset of the filter caps", GST_ELEMENT_NAME (self)); - - intersection = - gst_caps_intersect_full (template_caps, caps, GST_CAPS_INTERSECT_FIRST); - gst_caps_unref (caps); - caps = intersection; - } -#endif - - if (gst_caps_is_any (caps)) { - goto done; - } - - if (!gst_caps_is_fixed (caps)) { - g_assert (agg_klass->fixate_src_caps); - - GST_DEBUG_OBJECT (self, "fixate caps from %" GST_PTR_FORMAT, caps); - if (!(caps = agg_klass->fixate_src_caps (self, caps))) { - GST_WARNING_OBJECT (self, "Subclass failed to fixate provided caps"); - ret = GST_FLOW_NOT_NEGOTIATED; - goto done; - } - GST_DEBUG_OBJECT (self, " to %" GST_PTR_FORMAT, caps); - } - - if (agg_klass->negotiated_src_caps) { - if (!agg_klass->negotiated_src_caps (self, caps)) { - GST_WARNING_OBJECT (self, "Subclass failed to accept negotiated caps"); - ret = GST_FLOW_NOT_NEGOTIATED; - goto done; - } - } - - gst_aggregator_set_src_caps (self, caps); - - if (!gst_aggregator_do_allocation (self, caps)) { - GST_WARNING_OBJECT (self, "Allocation negotiation failed"); - ret = GST_FLOW_NOT_NEGOTIATED; - } - -done: - gst_caps_unref (downstream_caps); - gst_caps_unref (template_caps); - - if (caps) - gst_caps_unref (caps); - - return ret >= GST_FLOW_OK || ret == GST_AGGREGATOR_FLOW_NEED_DATA; -} - -/* WITH SRC_LOCK held */ -static gboolean -gst_aggregator_negotiate_unlocked (GstAggregator * self) -{ - GstAggregatorClass *agg_klass = GST_AGGREGATOR_GET_CLASS (self); - - if (agg_klass->negotiate) - return agg_klass->negotiate (self); - - return TRUE; -} - -/** - * gst_aggregator_negotiate: - * @self: a #GstAggregator - * - * Negotiates src pad caps with downstream elements. - * Unmarks GST_PAD_FLAG_NEED_RECONFIGURE in any case. But marks it again - * if #GstAggregatorClass::negotiate fails. - * - * Returns: %TRUE if the negotiation succeeded, else %FALSE. - * - * Since: 1.18 - */ -gboolean -gst_aggregator_negotiate (GstAggregator * self) -{ - gboolean ret = TRUE; - - g_return_val_if_fail (GST_IS_AGGREGATOR (self), FALSE); - - GST_PAD_STREAM_LOCK (GST_AGGREGATOR_SRC_PAD (self)); - gst_pad_check_reconfigure (GST_AGGREGATOR_SRC_PAD (self)); - ret = gst_aggregator_negotiate_unlocked (self); - if (!ret) - gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (self)); - GST_PAD_STREAM_UNLOCK (GST_AGGREGATOR_SRC_PAD (self)); - - return ret; -} - -static void -gst_aggregator_aggregate_func (GstAggregator * self) -{ - GstAggregatorPrivate *priv = self->priv; - GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (self); - gboolean timeout = FALSE; - - if (self->priv->running == FALSE) { - GST_DEBUG_OBJECT (self, "Not running anymore"); - return; - } - - GST_LOG_OBJECT (self, "Checking aggregate"); - while (priv->send_eos && priv->running) { - GstFlowReturn flow_return = GST_FLOW_OK; - DoHandleEventsAndQueriesData events_query_data = { FALSE, GST_FLOW_OK }; - - gst_element_foreach_sink_pad (GST_ELEMENT_CAST (self), - gst_aggregator_do_events_and_queries, &events_query_data); - - if ((flow_return = events_query_data.flow_ret) != GST_FLOW_OK) - goto handle_error; - - if (self->priv->peer_latency_live) - gst_element_foreach_sink_pad (GST_ELEMENT_CAST (self), - gst_aggregator_pad_skip_buffers, NULL); - - /* Ensure we have buffers ready (either in clipped_buffer or at the head of - * the queue */ - if (!gst_aggregator_wait_and_check (self, &timeout)) { - gst_element_foreach_sink_pad (GST_ELEMENT_CAST (self), - gst_aggregator_pad_reset_peeked_buffer, NULL); - continue; - } - - if (gst_pad_check_reconfigure (GST_AGGREGATOR_SRC_PAD (self))) { - if (!gst_aggregator_negotiate_unlocked (self)) { - gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (self)); - if (GST_PAD_IS_FLUSHING (GST_AGGREGATOR_SRC_PAD (self))) { - flow_return = GST_FLOW_FLUSHING; - } else { - flow_return = GST_FLOW_NOT_NEGOTIATED; - } - } - } - - if (timeout || flow_return >= GST_FLOW_OK) { - GST_TRACE_OBJECT (self, "Actually aggregating!"); - flow_return = klass->aggregate (self, timeout); - } - - gst_element_foreach_sink_pad (GST_ELEMENT_CAST (self), - gst_aggregator_pad_reset_peeked_buffer, NULL); - - if (!priv->selected_samples_called_or_warned) { - GST_FIXME_OBJECT (self, - "Subclass should call gst_aggregator_selected_samples() from its " - "aggregate implementation."); - priv->selected_samples_called_or_warned = TRUE; - } - - if (flow_return == GST_AGGREGATOR_FLOW_NEED_DATA) - continue; - - GST_OBJECT_LOCK (self); - if (flow_return == GST_FLOW_FLUSHING && priv->flushing) { - /* We don't want to set the pads to flushing, but we want to - * stop the thread, so just break here */ - GST_OBJECT_UNLOCK (self); - break; - } - GST_OBJECT_UNLOCK (self); - - if (flow_return == GST_FLOW_EOS || flow_return == GST_FLOW_ERROR) { - gst_aggregator_push_eos (self); - } - - handle_error: - GST_LOG_OBJECT (self, "flow return is %s", gst_flow_get_name (flow_return)); - - if (flow_return != GST_FLOW_OK) { - GList *item; - - GST_OBJECT_LOCK (self); - for (item = GST_ELEMENT (self)->sinkpads; item; item = item->next) { - GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (item->data); - - gst_aggregator_pad_set_flushing (aggpad, flow_return, TRUE); - } - GST_OBJECT_UNLOCK (self); - break; - } - } - - /* Pause the task here, the only ways to get here are: - * 1) We're stopping, in which case the task is stopped anyway - * 2) We got a flow error above, in which case it might take - * some time to forward the flow return upstream and we - * would otherwise call the task function over and over - * again without doing anything - */ - gst_pad_pause_task (self->srcpad); -} - -static gboolean -gst_aggregator_start (GstAggregator * self) -{ - GstAggregatorClass *klass; - gboolean result; - - self->priv->send_stream_start = TRUE; - self->priv->send_segment = TRUE; - self->priv->send_eos = TRUE; - self->priv->srccaps = NULL; - - self->priv->has_peer_latency = FALSE; - self->priv->peer_latency_live = FALSE; - self->priv->peer_latency_min = self->priv->peer_latency_max = 0; - - gst_aggregator_set_allocation (self, NULL, NULL, NULL, NULL); - - klass = GST_AGGREGATOR_GET_CLASS (self); - - if (klass->start) - result = klass->start (self); - else - result = TRUE; - - return result; -} - -static gboolean -gst_aggregator_stop_srcpad_task (GstAggregator * self, GstEvent * flush_start) -{ - gboolean res = TRUE; - - GST_INFO_OBJECT (self, "%s srcpad task", - flush_start ? "Pausing" : "Stopping"); - - SRC_LOCK (self); - self->priv->running = FALSE; - SRC_BROADCAST (self); - SRC_UNLOCK (self); - - if (flush_start) { - res = gst_pad_push_event (self->srcpad, flush_start); - } - - gst_pad_stop_task (self->srcpad); - - return res; -} - -static void -gst_aggregator_start_srcpad_task (GstAggregator * self) -{ - GST_INFO_OBJECT (self, "Starting srcpad task"); - - self->priv->running = TRUE; - gst_pad_start_task (GST_PAD (self->srcpad), - (GstTaskFunction) gst_aggregator_aggregate_func, self, NULL); -} - -static GstFlowReturn -gst_aggregator_flush (GstAggregator * self) -{ - GstFlowReturn ret = GST_FLOW_OK; - GstAggregatorPrivate *priv = self->priv; - GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (self); - - GST_DEBUG_OBJECT (self, "Flushing everything"); - GST_OBJECT_LOCK (self); - priv->send_segment = TRUE; - priv->flushing = FALSE; - priv->tags_changed = FALSE; - GST_OBJECT_UNLOCK (self); - if (klass->flush) - ret = klass->flush (self); - - return ret; -} - - -/* Called with GstAggregator's object lock held */ - -static gboolean -gst_aggregator_all_flush_stop_received (GstAggregator * self, guint32 seqnum) -{ - GList *tmp; - GstAggregatorPad *tmppad; - - for (tmp = GST_ELEMENT (self)->sinkpads; tmp; tmp = tmp->next) { - tmppad = (GstAggregatorPad *) tmp->data; - - if (tmppad->priv->last_flush_stop_seqnum != seqnum) - return FALSE; - } - - return TRUE; -} - -/* Called with GstAggregator's object lock held */ - -static gboolean -gst_aggregator_all_flush_start_received (GstAggregator * self, guint32 seqnum) -{ - GList *tmp; - GstAggregatorPad *tmppad; - - for (tmp = GST_ELEMENT (self)->sinkpads; tmp; tmp = tmp->next) { - tmppad = (GstAggregatorPad *) tmp->data; - - if (tmppad->priv->last_flush_start_seqnum != seqnum) { - return FALSE; - } - } - - return TRUE; -} - -static void -gst_aggregator_flush_start (GstAggregator * self, GstAggregatorPad * aggpad, - GstEvent * event) -{ - GstAggregatorPrivate *priv = self->priv; - GstAggregatorPadPrivate *padpriv = aggpad->priv; - guint32 seqnum = gst_event_get_seqnum (event); - - gst_aggregator_pad_set_flushing (aggpad, GST_FLOW_FLUSHING, FALSE); - - PAD_FLUSH_LOCK (aggpad); - PAD_LOCK (aggpad); - padpriv->last_flush_start_seqnum = seqnum; - PAD_UNLOCK (aggpad); - - GST_OBJECT_LOCK (self); - - if (!priv->flushing && gst_aggregator_all_flush_start_received (self, seqnum)) { - /* Make sure we don't forward more than one FLUSH_START */ - priv->flushing = TRUE; - priv->next_seqnum = seqnum; - GST_OBJECT_UNLOCK (self); - - GST_INFO_OBJECT (self, "Flushing, pausing srcpad task"); - gst_aggregator_stop_srcpad_task (self, event); - - event = NULL; - } else { - gst_event_unref (event); - GST_OBJECT_UNLOCK (self); - } - - PAD_FLUSH_UNLOCK (aggpad); -} - -/* Must be called with the the PAD_LOCK held */ -static void -update_time_level (GstAggregatorPad * aggpad, gboolean head) -{ - GstAggregatorPadPrivate *priv = aggpad->priv; - - if (head) { - if (GST_CLOCK_TIME_IS_VALID (priv->head_position) && - priv->head_segment.format == GST_FORMAT_TIME) - priv->head_time = gst_segment_to_running_time (&priv->head_segment, - GST_FORMAT_TIME, priv->head_position); - else - priv->head_time = GST_CLOCK_TIME_NONE; - - if (!GST_CLOCK_TIME_IS_VALID (priv->tail_time)) - priv->tail_time = priv->head_time; - } else { - if (GST_CLOCK_TIME_IS_VALID (priv->tail_position) && - aggpad->segment.format == GST_FORMAT_TIME) - priv->tail_time = gst_segment_to_running_time (&aggpad->segment, - GST_FORMAT_TIME, priv->tail_position); - else - priv->tail_time = priv->head_time; - } - - if (priv->head_time == GST_CLOCK_TIME_NONE || - priv->tail_time == GST_CLOCK_TIME_NONE) { - priv->time_level = 0; - return; - } - - if (priv->tail_time > priv->head_time) - priv->time_level = 0; - else - priv->time_level = priv->head_time - priv->tail_time; -} - - -/* GstAggregator vmethods default implementations */ -static gboolean -gst_aggregator_default_sink_event (GstAggregator * self, - GstAggregatorPad * aggpad, GstEvent * event) -{ - gboolean res = TRUE; - GstPad *pad = GST_PAD (aggpad); - GstAggregatorPrivate *priv = self->priv; - - GST_DEBUG_OBJECT (aggpad, "Got event: %" GST_PTR_FORMAT, event); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_START: - { - gst_aggregator_flush_start (self, aggpad, event); - /* We forward only in one case: right after flushing */ - event = NULL; - goto eat; - } - case GST_EVENT_FLUSH_STOP: - { - guint32 seqnum = gst_event_get_seqnum (event); - - PAD_FLUSH_LOCK (aggpad); - PAD_LOCK (aggpad); - aggpad->priv->last_flush_stop_seqnum = seqnum; - PAD_UNLOCK (aggpad); - - gst_aggregator_pad_flush (aggpad, self); - - GST_OBJECT_LOCK (self); - if (priv->flushing - && gst_aggregator_all_flush_stop_received (self, seqnum)) { - GST_OBJECT_UNLOCK (self); - /* That means we received FLUSH_STOP/FLUSH_STOP on - * all sinkpads -- Seeking is Done... sending FLUSH_STOP */ - gst_aggregator_flush (self); - gst_pad_push_event (self->srcpad, event); - event = NULL; - SRC_LOCK (self); - priv->send_eos = TRUE; - SRC_BROADCAST (self); - SRC_UNLOCK (self); - - GST_INFO_OBJECT (self, "Flush stopped"); - - gst_aggregator_start_srcpad_task (self); - } else { - GST_OBJECT_UNLOCK (self); - } - - PAD_FLUSH_UNLOCK (aggpad); - - /* We never forward the event */ - goto eat; - } - case GST_EVENT_EOS: - { - SRC_LOCK (self); - PAD_LOCK (aggpad); - g_assert (aggpad->priv->num_buffers == 0); - aggpad->priv->eos = TRUE; - PAD_UNLOCK (aggpad); - SRC_BROADCAST (self); - SRC_UNLOCK (self); - goto eat; - } - case GST_EVENT_SEGMENT: - { - PAD_LOCK (aggpad); - GST_OBJECT_LOCK (aggpad); - gst_event_copy_segment (event, &aggpad->segment); - /* We've got a new segment, tail_position is now meaningless - * and may interfere with the time_level calculation - */ - aggpad->priv->tail_position = GST_CLOCK_TIME_NONE; - update_time_level (aggpad, FALSE); - GST_OBJECT_UNLOCK (aggpad); - PAD_UNLOCK (aggpad); - - GST_OBJECT_LOCK (self); - self->priv->seqnum = gst_event_get_seqnum (event); - GST_OBJECT_UNLOCK (self); - goto eat; - } - case GST_EVENT_STREAM_START: - { - goto eat; - } - case GST_EVENT_GAP: - { - GstClockTime pts, endpts; - GstClockTime duration; - GstBuffer *gapbuf; - - gst_event_parse_gap (event, &pts, &duration); - - if (GST_CLOCK_TIME_IS_VALID (duration)) - endpts = pts + duration; - else - endpts = GST_CLOCK_TIME_NONE; - - GST_OBJECT_LOCK (aggpad); - res = gst_segment_clip (&aggpad->segment, GST_FORMAT_TIME, pts, endpts, - &pts, &endpts); - GST_OBJECT_UNLOCK (aggpad); - - if (!res) { - GST_WARNING_OBJECT (self, "GAP event outside segment, dropping"); - goto eat; - } - - if (GST_CLOCK_TIME_IS_VALID (endpts) && GST_CLOCK_TIME_IS_VALID (pts)) - duration = endpts - pts; - else - duration = GST_CLOCK_TIME_NONE; - - gapbuf = gst_buffer_new (); - GST_BUFFER_PTS (gapbuf) = pts; - GST_BUFFER_DURATION (gapbuf) = duration; - GST_BUFFER_FLAG_SET (gapbuf, GST_BUFFER_FLAG_GAP); - GST_BUFFER_FLAG_SET (gapbuf, GST_BUFFER_FLAG_DROPPABLE); - - /* Remove GAP event so we can replace it with the buffer */ - PAD_LOCK (aggpad); - if (g_queue_peek_tail (&aggpad->priv->data) == event) - gst_event_unref (g_queue_pop_tail (&aggpad->priv->data)); - PAD_UNLOCK (aggpad); - - if (gst_aggregator_pad_chain_internal (self, aggpad, gapbuf, FALSE) != - GST_FLOW_OK) { - GST_WARNING_OBJECT (self, "Failed to chain gap buffer"); - res = FALSE; - } - - goto eat; - } - case GST_EVENT_TAG: - goto eat; - default: - { - break; - } - } - - GST_DEBUG_OBJECT (pad, "Forwarding event: %" GST_PTR_FORMAT, event); - return gst_pad_event_default (pad, GST_OBJECT (self), event); - -eat: - GST_DEBUG_OBJECT (pad, "Eating event: %" GST_PTR_FORMAT, event); - if (event) - gst_event_unref (event); - - return res; -} - -/* Queue serialized events and let the others go through directly. - * The queued events with be handled from the src-pad task in - * gst_aggregator_do_events_and_queries(). - */ -static GstFlowReturn -gst_aggregator_default_sink_event_pre_queue (GstAggregator * self, - GstAggregatorPad * aggpad, GstEvent * event) -{ - GstFlowReturn ret = GST_FLOW_OK; - - if (GST_EVENT_IS_SERIALIZED (event) - && GST_EVENT_TYPE (event) != GST_EVENT_FLUSH_STOP) { - SRC_LOCK (self); - PAD_LOCK (aggpad); - - if (aggpad->priv->flow_return != GST_FLOW_OK) - goto flushing; - - if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) { - GST_OBJECT_LOCK (aggpad); - gst_event_copy_segment (event, &aggpad->priv->head_segment); - aggpad->priv->head_position = aggpad->priv->head_segment.position; - update_time_level (aggpad, TRUE); - GST_OBJECT_UNLOCK (aggpad); - } - - GST_DEBUG_OBJECT (aggpad, "Store event in queue: %" GST_PTR_FORMAT, event); - g_queue_push_head (&aggpad->priv->data, event); - SRC_BROADCAST (self); - PAD_UNLOCK (aggpad); - SRC_UNLOCK (self); - } else { - GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (self); - - if (!klass->sink_event (self, aggpad, event)) { - /* Copied from GstPad to convert boolean to a GstFlowReturn in - * the event handling func */ - ret = GST_FLOW_ERROR; - } - } - - return ret; - -flushing: - GST_DEBUG_OBJECT (aggpad, "Pad is %s, dropping event", - gst_flow_get_name (aggpad->priv->flow_return)); - PAD_UNLOCK (aggpad); - SRC_UNLOCK (self); - if (GST_EVENT_IS_STICKY (event)) - gst_pad_store_sticky_event (GST_PAD (aggpad), event); - gst_event_unref (event); - - return aggpad->priv->flow_return; -} - -static gboolean -gst_aggregator_stop_pad (GstElement * self, GstPad * epad, gpointer user_data) -{ - GstAggregatorPad *pad = GST_AGGREGATOR_PAD_CAST (epad); - GstAggregator *agg = GST_AGGREGATOR_CAST (self); - - gst_aggregator_pad_flush (pad, agg); - - PAD_LOCK (pad); - pad->priv->flow_return = GST_FLOW_FLUSHING; - pad->priv->negotiated = FALSE; - PAD_BROADCAST_EVENT (pad); - PAD_UNLOCK (pad); - - return TRUE; -} - -static gboolean -gst_aggregator_stop (GstAggregator * agg) -{ - GstAggregatorClass *klass; - gboolean result; - - gst_aggregator_reset_flow_values (agg); - - /* Application needs to make sure no pads are added while it shuts us down */ - gst_element_foreach_sink_pad (GST_ELEMENT_CAST (agg), - gst_aggregator_stop_pad, NULL); - - klass = GST_AGGREGATOR_GET_CLASS (agg); - - if (klass->stop) - result = klass->stop (agg); - else - result = TRUE; - - agg->priv->has_peer_latency = FALSE; - agg->priv->peer_latency_live = FALSE; - agg->priv->peer_latency_min = agg->priv->peer_latency_max = 0; - - if (agg->priv->tags) - gst_tag_list_unref (agg->priv->tags); - agg->priv->tags = NULL; - - gst_aggregator_set_allocation (agg, NULL, NULL, NULL, NULL); - - if (agg->priv->running) { - /* As sinkpads get deactivated after the src pad, we - * may have restarted the source pad task after receiving - * flush events on one of our sinkpads. Stop our src pad - * task again if that is the case */ - gst_aggregator_stop_srcpad_task (agg, NULL); - } - - return result; -} - -/* GstElement vmethods implementations */ -static GstStateChangeReturn -gst_aggregator_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn ret; - GstAggregator *self = GST_AGGREGATOR (element); - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - if (!gst_aggregator_start (self)) - goto error_start; - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - /* Wake up any waiting as now we have a clock and can do - * proper waiting on the clock if necessary */ - SRC_LOCK (self); - SRC_BROADCAST (self); - SRC_UNLOCK (self); - break; - default: - break; - } - - if ((ret = - GST_ELEMENT_CLASS (aggregator_parent_class)->change_state (element, - transition)) == GST_STATE_CHANGE_FAILURE) - goto failure; - - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - if (!gst_aggregator_stop (self)) { - /* What to do in this case? Error out? */ - GST_ERROR_OBJECT (self, "Subclass failed to stop."); - } - break; - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - /* Wake up any waiting as now clock might be gone and we might - * need to wait on the condition variable again */ - SRC_LOCK (self); - SRC_BROADCAST (self); - SRC_UNLOCK (self); - break; - default: - break; - } - - return ret; - -/* ERRORS */ -failure: - { - GST_ERROR_OBJECT (element, "parent failed state change"); - return ret; - } -error_start: - { - GST_ERROR_OBJECT (element, "Subclass failed to start"); - return GST_STATE_CHANGE_FAILURE; - } -} - -static void -gst_aggregator_release_pad (GstElement * element, GstPad * pad) -{ - GstAggregator *self = GST_AGGREGATOR (element); - GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad); - - GST_INFO_OBJECT (pad, "Removing pad"); - - SRC_LOCK (self); - gst_aggregator_pad_set_flushing (aggpad, GST_FLOW_FLUSHING, TRUE); - gst_buffer_replace (&aggpad->priv->peeked_buffer, NULL); - gst_element_remove_pad (element, pad); - - self->priv->has_peer_latency = FALSE; - SRC_BROADCAST (self); - SRC_UNLOCK (self); -} - -static GstAggregatorPad * -gst_aggregator_default_create_new_pad (GstAggregator * self, - GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps) -{ - GstAggregatorPad *agg_pad; - GstAggregatorPrivate *priv = self->priv; - gint serial = 0; - gchar *name = NULL; - GType pad_type = - GST_PAD_TEMPLATE_GTYPE (templ) == - G_TYPE_NONE ? GST_TYPE_AGGREGATOR_PAD : GST_PAD_TEMPLATE_GTYPE (templ); - - if (templ->direction != GST_PAD_SINK) - goto not_sink; - - if (templ->presence != GST_PAD_REQUEST) - goto not_request; - - GST_OBJECT_LOCK (self); - if (req_name == NULL || strlen (req_name) < 6 - || !g_str_has_prefix (req_name, "sink_") - || strrchr (req_name, '%') != NULL) { - /* no name given when requesting the pad, use next available int */ - serial = ++priv->max_padserial; - } else { - gchar *endptr = NULL; - - /* parse serial number from requested padname */ - serial = g_ascii_strtoull (&req_name[5], &endptr, 10); - if (endptr != NULL && *endptr == '\0') { - if (serial > priv->max_padserial) { - priv->max_padserial = serial; - } - } else { - serial = ++priv->max_padserial; - } - } - - name = g_strdup_printf ("sink_%u", serial); - g_assert (g_type_is_a (pad_type, GST_TYPE_AGGREGATOR_PAD)); - agg_pad = g_object_new (pad_type, - "name", name, "direction", GST_PAD_SINK, "template", templ, NULL); - g_free (name); - - GST_OBJECT_UNLOCK (self); - - return agg_pad; - - /* errors */ -not_sink: - { - GST_WARNING_OBJECT (self, "request new pad that is not a SINK pad"); - return NULL; - } -not_request: - { - GST_WARNING_OBJECT (self, "request new pad that is not a REQUEST pad"); - return NULL; - } -} - -static GstPad * -gst_aggregator_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps) -{ - GstAggregator *self; - GstAggregatorPad *agg_pad; - GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (element); - GstAggregatorPrivate *priv = GST_AGGREGATOR (element)->priv; - - self = GST_AGGREGATOR (element); - - agg_pad = klass->create_new_pad (self, templ, req_name, caps); - if (!agg_pad) { - GST_ERROR_OBJECT (element, "Couldn't create new pad"); - return NULL; - } - - GST_DEBUG_OBJECT (element, "Adding pad %s", GST_PAD_NAME (agg_pad)); - - if (priv->running) - gst_pad_set_active (GST_PAD (agg_pad), TRUE); - - /* add the pad to the element */ - gst_element_add_pad (element, GST_PAD (agg_pad)); - - return GST_PAD (agg_pad); -} - -/* Must be called with SRC_LOCK held, temporarily releases it! */ - -static gboolean -gst_aggregator_query_latency_unlocked (GstAggregator * self, GstQuery * query) -{ - gboolean query_ret, live; - GstClockTime our_latency, min, max; - - /* Temporarily release the lock to do the query. */ - SRC_UNLOCK (self); - query_ret = gst_pad_query_default (self->srcpad, GST_OBJECT (self), query); - SRC_LOCK (self); - - if (!query_ret) { - GST_WARNING_OBJECT (self, "Latency query failed"); - return FALSE; - } - - gst_query_parse_latency (query, &live, &min, &max); - - if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (min))) { - GST_ERROR_OBJECT (self, "Invalid minimum latency %" GST_TIME_FORMAT - ". Please file a bug at " PACKAGE_BUGREPORT ".", GST_TIME_ARGS (min)); - return FALSE; - } - - if (self->priv->upstream_latency_min > min) { - GstClockTimeDiff diff = - GST_CLOCK_DIFF (min, self->priv->upstream_latency_min); - - min += diff; - if (GST_CLOCK_TIME_IS_VALID (max)) { - max += diff; - } - } - - if (min > max && GST_CLOCK_TIME_IS_VALID (max)) { - SRC_UNLOCK (self); - GST_ELEMENT_WARNING (self, CORE, CLOCK, (NULL), - ("Impossible to configure latency: max %" GST_TIME_FORMAT " < min %" - GST_TIME_FORMAT ". Add queues or other buffering elements.", - GST_TIME_ARGS (max), GST_TIME_ARGS (min))); - SRC_LOCK (self); - return FALSE; - } - - our_latency = self->priv->latency; - - self->priv->peer_latency_live = live; - self->priv->peer_latency_min = min; - self->priv->peer_latency_max = max; - self->priv->has_peer_latency = TRUE; - - /* add our own */ - min += our_latency; - min += self->priv->sub_latency_min; - if (GST_CLOCK_TIME_IS_VALID (self->priv->sub_latency_max) - && GST_CLOCK_TIME_IS_VALID (max)) - max += self->priv->sub_latency_max + our_latency; - else - max = GST_CLOCK_TIME_NONE; - - SRC_BROADCAST (self); - - GST_DEBUG_OBJECT (self, "configured latency live:%s min:%" G_GINT64_FORMAT - " max:%" G_GINT64_FORMAT, live ? "true" : "false", min, max); - - gst_query_set_latency (query, live, min, max); - - return query_ret; -} - -/* - * MUST be called with the src_lock held. Temporarily releases the lock inside - * gst_aggregator_query_latency_unlocked() to do the actual query! - * - * See gst_aggregator_get_latency() for doc - */ -static GstClockTime -gst_aggregator_get_latency_unlocked (GstAggregator * self) -{ - GstClockTime latency; - - g_return_val_if_fail (GST_IS_AGGREGATOR (self), 0); - - if (!self->priv->has_peer_latency) { - GstQuery *query = gst_query_new_latency (); - gboolean ret; - - ret = gst_aggregator_query_latency_unlocked (self, query); - gst_query_unref (query); - if (!ret) - return GST_CLOCK_TIME_NONE; - } - - if (!self->priv->has_peer_latency || !self->priv->peer_latency_live) - return GST_CLOCK_TIME_NONE; - - /* latency_min is never GST_CLOCK_TIME_NONE by construction */ - latency = self->priv->peer_latency_min; - - /* add our own */ - latency += self->priv->latency; - latency += self->priv->sub_latency_min; - - return latency; -} - -/** - * gst_aggregator_get_latency: - * @self: a #GstAggregator - * - * Retrieves the latency values reported by @self in response to the latency - * query, or %GST_CLOCK_TIME_NONE if there is not live source connected and the element - * will not wait for the clock. - * - * Typically only called by subclasses. - * - * Returns: The latency or %GST_CLOCK_TIME_NONE if the element does not sync - */ -GstClockTime -gst_aggregator_get_latency (GstAggregator * self) -{ - GstClockTime ret; - - SRC_LOCK (self); - ret = gst_aggregator_get_latency_unlocked (self); - SRC_UNLOCK (self); - - return ret; -} - -static gboolean -gst_aggregator_send_event (GstElement * element, GstEvent * event) -{ - GstAggregator *self = GST_AGGREGATOR (element); - - GST_STATE_LOCK (element); - if (GST_EVENT_TYPE (event) == GST_EVENT_SEEK && - GST_STATE (element) < GST_STATE_PAUSED) { - gdouble rate; - GstFormat fmt; - GstSeekFlags flags; - GstSeekType start_type, stop_type; - gint64 start, stop; - - gst_event_parse_seek (event, &rate, &fmt, &flags, &start_type, - &start, &stop_type, &stop); - - GST_OBJECT_LOCK (self); - gst_segment_do_seek (&GST_AGGREGATOR_PAD (self->srcpad)->segment, rate, fmt, - flags, start_type, start, stop_type, stop, NULL); - self->priv->next_seqnum = gst_event_get_seqnum (event); - self->priv->first_buffer = FALSE; - GST_OBJECT_UNLOCK (self); - - GST_DEBUG_OBJECT (element, "Storing segment %" GST_PTR_FORMAT, event); - } - GST_STATE_UNLOCK (element); - - return GST_ELEMENT_CLASS (aggregator_parent_class)->send_event (element, - event); -} - -static gboolean -gst_aggregator_default_src_query (GstAggregator * self, GstQuery * query) -{ - gboolean res = TRUE; - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_SEEKING: - { - GstFormat format; - - /* don't pass it along as some (file)sink might claim it does - * whereas with a collectpads in between that will not likely work */ - gst_query_parse_seeking (query, &format, NULL, NULL, NULL); - gst_query_set_seeking (query, format, FALSE, 0, -1); - res = TRUE; - - break; - } - case GST_QUERY_LATENCY: - SRC_LOCK (self); - res = gst_aggregator_query_latency_unlocked (self, query); - SRC_UNLOCK (self); - break; - default: - return gst_pad_query_default (self->srcpad, GST_OBJECT (self), query); - } - - return res; -} - -static gboolean -gst_aggregator_event_forward_func (GstPad * pad, gpointer user_data) -{ - EventData *evdata = user_data; - gboolean ret = TRUE; - GstPad *peer = gst_pad_get_peer (pad); - GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad); - - if (peer) { - if (evdata->only_to_active_pads && aggpad->priv->first_buffer) { - GST_DEBUG_OBJECT (pad, "not sending event to inactive pad"); - ret = TRUE; - } else { - ret = gst_pad_send_event (peer, gst_event_ref (evdata->event)); - GST_DEBUG_OBJECT (pad, "return of event push is %d", ret); - } - } - - if (ret == FALSE) { - if (GST_EVENT_TYPE (evdata->event) == GST_EVENT_SEEK) { - GstQuery *seeking = gst_query_new_seeking (GST_FORMAT_TIME); - - GST_DEBUG_OBJECT (pad, "Event %" GST_PTR_FORMAT " failed", evdata->event); - - if (gst_pad_query (peer, seeking)) { - gboolean seekable; - - gst_query_parse_seeking (seeking, NULL, &seekable, NULL, NULL); - - if (seekable == FALSE) { - GST_INFO_OBJECT (pad, - "Source not seekable, We failed but it does not matter!"); - - ret = TRUE; - } - } else { - GST_ERROR_OBJECT (pad, "Query seeking FAILED"); - } - - gst_query_unref (seeking); - } - } else { - evdata->one_actually_seeked = TRUE; - } - - evdata->result &= ret; - - if (peer) - gst_object_unref (peer); - - /* Always send to all pads */ - return FALSE; -} - -static void -gst_aggregator_forward_event_to_all_sinkpads (GstAggregator * self, - EventData * evdata) -{ - evdata->result = TRUE; - evdata->one_actually_seeked = FALSE; - - gst_pad_forward (self->srcpad, gst_aggregator_event_forward_func, evdata); - - gst_event_unref (evdata->event); -} - -static gboolean -gst_aggregator_do_seek (GstAggregator * self, GstEvent * event) -{ - gdouble rate; - GstFormat fmt; - GstSeekFlags flags; - GstSeekType start_type, stop_type; - gint64 start, stop; - gboolean flush; - EventData evdata = { 0, }; - GstAggregatorPrivate *priv = self->priv; - - gst_event_parse_seek (event, &rate, &fmt, &flags, &start_type, - &start, &stop_type, &stop); - - GST_INFO_OBJECT (self, "starting SEEK"); - - flush = flags & GST_SEEK_FLAG_FLUSH; - - GST_OBJECT_LOCK (self); - - if (gst_event_get_seqnum (event) == self->priv->next_seqnum) { - evdata.result = TRUE; - GST_DEBUG_OBJECT (self, "Dropping duplicated seek event with seqnum %d", - self->priv->next_seqnum); - GST_OBJECT_UNLOCK (self); - goto done; - } - - self->priv->next_seqnum = gst_event_get_seqnum (event); - - gst_segment_do_seek (&GST_AGGREGATOR_PAD (self->srcpad)->segment, rate, fmt, - flags, start_type, start, stop_type, stop, NULL); - - /* Seeking sets a position */ - self->priv->first_buffer = FALSE; - - if (flush) - priv->flushing = TRUE; - - GST_OBJECT_UNLOCK (self); - - if (flush) { - GstEvent *event = gst_event_new_flush_start (); - - gst_event_set_seqnum (event, self->priv->next_seqnum); - gst_aggregator_stop_srcpad_task (self, event); - } - - /* forward the seek upstream */ - evdata.event = event; - evdata.flush = flush; - evdata.only_to_active_pads = FALSE; - gst_aggregator_forward_event_to_all_sinkpads (self, &evdata); - event = NULL; - - if (!evdata.result || !evdata.one_actually_seeked) { - GST_OBJECT_LOCK (self); - priv->flushing = FALSE; - GST_OBJECT_UNLOCK (self); - - /* No flush stop is inbound for us to forward */ - if (flush) { - GstEvent *event = gst_event_new_flush_stop (TRUE); - - gst_event_set_seqnum (event, self->priv->next_seqnum); - gst_pad_push_event (self->srcpad, event); - } - } - -done: - GST_INFO_OBJECT (self, "seek done, result: %d", evdata.result); - - return evdata.result; -} - -static gboolean -gst_aggregator_default_src_event (GstAggregator * self, GstEvent * event) -{ - EventData evdata = { 0, }; - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - /* _do_seek() unrefs the event. */ - return gst_aggregator_do_seek (self, event); - case GST_EVENT_NAVIGATION: - /* navigation is rather pointless. */ - gst_event_unref (event); - return FALSE; - default: - break; - } - - /* Don't forward QOS events to pads that had no active buffer yet. Otherwise - * they will receive a QOS event that has earliest_time=0 (because we can't - * have negative timestamps), and consider their buffer as too late */ - evdata.event = event; - evdata.flush = FALSE; - evdata.only_to_active_pads = GST_EVENT_TYPE (event) == GST_EVENT_QOS; - gst_aggregator_forward_event_to_all_sinkpads (self, &evdata); - return evdata.result; -} - -static gboolean -gst_aggregator_src_pad_event_func (GstPad * pad, GstObject * parent, - GstEvent * event) -{ - GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (parent); - - return klass->src_event (GST_AGGREGATOR (parent), event); -} - -static gboolean -gst_aggregator_src_pad_query_func (GstPad * pad, GstObject * parent, - GstQuery * query) -{ - GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (parent); - - return klass->src_query (GST_AGGREGATOR (parent), query); -} - -static gboolean -gst_aggregator_src_pad_activate_mode_func (GstPad * pad, - GstObject * parent, GstPadMode mode, gboolean active) -{ - GstAggregator *self = GST_AGGREGATOR (parent); - GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (parent); - - if (klass->src_activate) { - if (klass->src_activate (self, mode, active) == FALSE) { - return FALSE; - } - } - - if (active == TRUE) { - switch (mode) { - case GST_PAD_MODE_PUSH: - { - GST_INFO_OBJECT (pad, "Activating pad!"); - gst_aggregator_start_srcpad_task (self); - return TRUE; - } - default: - { - GST_ERROR_OBJECT (pad, "Only supported mode is PUSH"); - return FALSE; - } - } - } - - /* deactivating */ - GST_INFO_OBJECT (self, "Deactivating srcpad"); - - gst_aggregator_stop_srcpad_task (self, FALSE); - - return TRUE; -} - -static gboolean -gst_aggregator_default_sink_query (GstAggregator * self, - GstAggregatorPad * aggpad, GstQuery * query) -{ - GstPad *pad = GST_PAD (aggpad); - - if (GST_QUERY_TYPE (query) == GST_QUERY_ALLOCATION) { - GstQuery *decide_query = NULL; - GstAggregatorClass *agg_class; - gboolean ret; - - GST_OBJECT_LOCK (self); - PAD_LOCK (aggpad); - if (G_UNLIKELY (!aggpad->priv->negotiated)) { - GST_DEBUG_OBJECT (self, - "not negotiated yet, can't answer ALLOCATION query"); - PAD_UNLOCK (aggpad); - GST_OBJECT_UNLOCK (self); - - return FALSE; - } - - if ((decide_query = self->priv->allocation_query)) - gst_query_ref (decide_query); - PAD_UNLOCK (aggpad); - GST_OBJECT_UNLOCK (self); - - GST_DEBUG_OBJECT (self, - "calling propose allocation with query %" GST_PTR_FORMAT, decide_query); - - agg_class = GST_AGGREGATOR_GET_CLASS (self); - - /* pass the query to the propose_allocation vmethod if any */ - if (agg_class->propose_allocation) - ret = agg_class->propose_allocation (self, aggpad, decide_query, query); - else - ret = FALSE; - - if (decide_query) - gst_query_unref (decide_query); - - GST_DEBUG_OBJECT (self, "ALLOCATION ret %d, %" GST_PTR_FORMAT, ret, query); - return ret; - } - - return gst_pad_query_default (pad, GST_OBJECT (self), query); -} - -static gboolean -gst_aggregator_default_sink_query_pre_queue (GstAggregator * self, - GstAggregatorPad * aggpad, GstQuery * query) -{ - if (GST_QUERY_IS_SERIALIZED (query)) { - GstStructure *s; - gboolean ret = FALSE; - - SRC_LOCK (self); - PAD_LOCK (aggpad); - - if (aggpad->priv->flow_return != GST_FLOW_OK) { - SRC_UNLOCK (self); - goto flushing; - } - - g_queue_push_head (&aggpad->priv->data, query); - SRC_BROADCAST (self); - SRC_UNLOCK (self); - - while (!gst_aggregator_pad_queue_is_empty (aggpad) - && aggpad->priv->flow_return == GST_FLOW_OK) { - GST_DEBUG_OBJECT (aggpad, "Waiting for buffer to be consumed"); - PAD_WAIT_EVENT (aggpad); - } - - s = gst_query_writable_structure (query); - if (gst_structure_get_boolean (s, "gst-aggregator-retval", &ret)) - gst_structure_remove_field (s, "gst-aggregator-retval"); - else - g_queue_remove (&aggpad->priv->data, query); - - if (aggpad->priv->flow_return != GST_FLOW_OK) - goto flushing; - - PAD_UNLOCK (aggpad); - - return ret; - } else { - GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (self); - - return klass->sink_query (self, aggpad, query); - } - -flushing: - GST_DEBUG_OBJECT (aggpad, "Pad is %s, dropping query", - gst_flow_get_name (aggpad->priv->flow_return)); - PAD_UNLOCK (aggpad); - - return FALSE; -} - -static void -gst_aggregator_finalize (GObject * object) -{ - GstAggregator *self = (GstAggregator *) object; - - g_mutex_clear (&self->priv->src_lock); - g_cond_clear (&self->priv->src_cond); - - G_OBJECT_CLASS (aggregator_parent_class)->finalize (object); -} - -/* - * gst_aggregator_set_latency_property: - * @agg: a #GstAggregator - * @latency: the new latency value (in nanoseconds). - * - * Sets the new latency value to @latency. This value is used to limit the - * amount of time a pad waits for data to appear before considering the pad - * as unresponsive. - */ -static void -gst_aggregator_set_latency_property (GstAggregator * self, GstClockTime latency) -{ - gboolean changed; - - g_return_if_fail (GST_IS_AGGREGATOR (self)); - g_return_if_fail (GST_CLOCK_TIME_IS_VALID (latency)); - - SRC_LOCK (self); - changed = (self->priv->latency != latency); - - if (changed) { - GList *item; - - GST_OBJECT_LOCK (self); - /* First lock all the pads */ - for (item = GST_ELEMENT_CAST (self)->sinkpads; item; item = item->next) { - GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (item->data); - PAD_LOCK (aggpad); - } - - self->priv->latency = latency; - - SRC_BROADCAST (self); - - /* Now wake up the pads */ - for (item = GST_ELEMENT_CAST (self)->sinkpads; item; item = item->next) { - GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (item->data); - PAD_BROADCAST_EVENT (aggpad); - PAD_UNLOCK (aggpad); - } - GST_OBJECT_UNLOCK (self); - } - - SRC_UNLOCK (self); - - if (changed) - gst_element_post_message (GST_ELEMENT_CAST (self), - gst_message_new_latency (GST_OBJECT_CAST (self))); -} - -/* - * gst_aggregator_get_latency_property: - * @agg: a #GstAggregator - * - * Gets the latency value. See gst_aggregator_set_latency for - * more details. - * - * Returns: The time in nanoseconds to wait for data to arrive on a sink pad - * before a pad is deemed unresponsive. A value of -1 means an - * unlimited time. - */ -static GstClockTime -gst_aggregator_get_latency_property (GstAggregator * agg) -{ - GstClockTime res; - - g_return_val_if_fail (GST_IS_AGGREGATOR (agg), GST_CLOCK_TIME_NONE); - - GST_OBJECT_LOCK (agg); - res = agg->priv->latency; - GST_OBJECT_UNLOCK (agg); - - return res; -} - -static void -gst_aggregator_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstAggregator *agg = GST_AGGREGATOR (object); - - switch (prop_id) { - case PROP_LATENCY: - gst_aggregator_set_latency_property (agg, g_value_get_uint64 (value)); - break; - case PROP_MIN_UPSTREAM_LATENCY: - SRC_LOCK (agg); - agg->priv->upstream_latency_min = g_value_get_uint64 (value); - SRC_UNLOCK (agg); - break; - case PROP_START_TIME_SELECTION: - agg->priv->start_time_selection = g_value_get_enum (value); - break; - case PROP_START_TIME: - agg->priv->start_time = g_value_get_uint64 (value); - break; - case PROP_EMIT_SIGNALS: - agg->priv->emit_signals = g_value_get_boolean (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_aggregator_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstAggregator *agg = GST_AGGREGATOR (object); - - switch (prop_id) { - case PROP_LATENCY: - g_value_set_uint64 (value, gst_aggregator_get_latency_property (agg)); - break; - case PROP_MIN_UPSTREAM_LATENCY: - SRC_LOCK (agg); - g_value_set_uint64 (value, agg->priv->upstream_latency_min); - SRC_UNLOCK (agg); - break; - case PROP_START_TIME_SELECTION: - g_value_set_enum (value, agg->priv->start_time_selection); - break; - case PROP_START_TIME: - g_value_set_uint64 (value, agg->priv->start_time); - break; - case PROP_EMIT_SIGNALS: - g_value_set_boolean (value, agg->priv->emit_signals); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/* GObject vmethods implementations */ -static void -gst_aggregator_class_init (GstAggregatorClass * klass) -{ - GObjectClass *gobject_class = (GObjectClass *) klass; - GstElementClass *gstelement_class = (GstElementClass *) klass; - - aggregator_parent_class = g_type_class_peek_parent (klass); - - GST_DEBUG_CATEGORY_INIT (aggregator_debug, "aggregator", - GST_DEBUG_FG_MAGENTA, "GstAggregator"); - - if (aggregator_private_offset != 0) - g_type_class_adjust_private_offset (klass, &aggregator_private_offset); - - klass->finish_buffer = gst_aggregator_default_finish_buffer; - klass->finish_buffer_list = gst_aggregator_default_finish_buffer_list; - - klass->sink_event = gst_aggregator_default_sink_event; - klass->sink_query = gst_aggregator_default_sink_query; - - klass->src_event = gst_aggregator_default_src_event; - klass->src_query = gst_aggregator_default_src_query; - - klass->create_new_pad = gst_aggregator_default_create_new_pad; - klass->update_src_caps = gst_aggregator_default_update_src_caps; - klass->fixate_src_caps = gst_aggregator_default_fixate_src_caps; - klass->negotiated_src_caps = gst_aggregator_default_negotiated_src_caps; - - klass->negotiate = gst_aggregator_default_negotiate; - - klass->sink_event_pre_queue = gst_aggregator_default_sink_event_pre_queue; - klass->sink_query_pre_queue = gst_aggregator_default_sink_query_pre_queue; - - gstelement_class->request_new_pad = - GST_DEBUG_FUNCPTR (gst_aggregator_request_new_pad); - gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_aggregator_send_event); - gstelement_class->release_pad = - GST_DEBUG_FUNCPTR (gst_aggregator_release_pad); - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_aggregator_change_state); - - gobject_class->set_property = gst_aggregator_set_property; - gobject_class->get_property = gst_aggregator_get_property; - gobject_class->finalize = gst_aggregator_finalize; - - g_object_class_install_property (gobject_class, PROP_LATENCY, - g_param_spec_uint64 ("latency", "Buffer latency", - "Additional latency in live mode to allow upstream " - "to take longer to produce buffers for the current " - "position (in nanoseconds)", 0, G_MAXUINT64, - DEFAULT_LATENCY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - /** - * GstAggregator:min-upstream-latency: - * - * Force minimum upstream latency (in nanoseconds). When sources with a - * higher latency are expected to be plugged in dynamically after the - * aggregator has started playing, this allows overriding the minimum - * latency reported by the initial source(s). This is only taken into - * account when larger than the actually reported minimum latency. - * - * Since: 1.16 - */ - g_object_class_install_property (gobject_class, PROP_MIN_UPSTREAM_LATENCY, - g_param_spec_uint64 ("min-upstream-latency", "Buffer latency", - "When sources with a higher latency are expected to be plugged " - "in dynamically after the aggregator has started playing, " - "this allows overriding the minimum latency reported by the " - "initial source(s). This is only taken into account when larger " - "than the actually reported minimum latency. (nanoseconds)", - 0, G_MAXUINT64, - DEFAULT_LATENCY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_START_TIME_SELECTION, - g_param_spec_enum ("start-time-selection", "Start Time Selection", - "Decides which start time is output", - gst_aggregator_start_time_selection_get_type (), - DEFAULT_START_TIME_SELECTION, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_START_TIME, - g_param_spec_uint64 ("start-time", "Start Time", - "Start time to use if start-time-selection=set", 0, - G_MAXUINT64, - DEFAULT_START_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - /** - * GstAggregator:emit-signals: - * - * Enables the emission of signals such as #GstAggregator::samples-selected - * - * Since: 1.18 - */ - g_object_class_install_property (gobject_class, PROP_EMIT_SIGNALS, - g_param_spec_boolean ("emit-signals", "Emit signals", - "Send signals", DEFAULT_EMIT_SIGNALS, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - /** - * GstAggregator::samples-selected: - * @aggregator: The #GstAggregator that emitted the signal - * @segment: The #GstSegment the next output buffer is part of - * @pts: The presentation timestamp of the next output buffer - * @dts: The decoding timestamp of the next output buffer - * @duration: The duration of the next output buffer - * @info: (nullable): a #GstStructure containing additional information - * - * Signals that the #GstAggregator subclass has selected the next set - * of input samples it will aggregate. Handlers may call - * gst_aggregator_peek_next_sample() at that point. - * - * Since: 1.18 - */ - gst_aggregator_signals[SIGNAL_SAMPLES_SELECTED] = - g_signal_new ("samples-selected", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 5, - GST_TYPE_SEGMENT | G_SIGNAL_TYPE_STATIC_SCOPE, GST_TYPE_CLOCK_TIME, - GST_TYPE_CLOCK_TIME, GST_TYPE_CLOCK_TIME, - GST_TYPE_STRUCTURE | G_SIGNAL_TYPE_STATIC_SCOPE); -} - -static inline gpointer -gst_aggregator_get_instance_private (GstAggregator * self) -{ - return (G_STRUCT_MEMBER_P (self, aggregator_private_offset)); -} - -static void -gst_aggregator_init (GstAggregator * self, GstAggregatorClass * klass) -{ - GstPadTemplate *pad_template; - GstAggregatorPrivate *priv; - GType pad_type; - - g_return_if_fail (klass->aggregate != NULL); - - self->priv = gst_aggregator_get_instance_private (self); - - priv = self->priv; - - pad_template = - gst_element_class_get_pad_template (GST_ELEMENT_CLASS (klass), "src"); - g_return_if_fail (pad_template != NULL); - - priv->max_padserial = -1; - priv->tags_changed = FALSE; - - self->priv->peer_latency_live = FALSE; - self->priv->peer_latency_min = self->priv->sub_latency_min = 0; - self->priv->peer_latency_max = self->priv->sub_latency_max = 0; - self->priv->has_peer_latency = FALSE; - - pad_type = - GST_PAD_TEMPLATE_GTYPE (pad_template) == - G_TYPE_NONE ? GST_TYPE_AGGREGATOR_PAD : - GST_PAD_TEMPLATE_GTYPE (pad_template); - g_assert (g_type_is_a (pad_type, GST_TYPE_AGGREGATOR_PAD)); - self->srcpad = - g_object_new (pad_type, "name", "src", "direction", GST_PAD_SRC, - "template", pad_template, NULL); - - gst_aggregator_reset_flow_values (self); - - gst_pad_set_event_function (self->srcpad, - GST_DEBUG_FUNCPTR (gst_aggregator_src_pad_event_func)); - gst_pad_set_query_function (self->srcpad, - GST_DEBUG_FUNCPTR (gst_aggregator_src_pad_query_func)); - gst_pad_set_activatemode_function (self->srcpad, - GST_DEBUG_FUNCPTR (gst_aggregator_src_pad_activate_mode_func)); - - gst_element_add_pad (GST_ELEMENT (self), self->srcpad); - - self->priv->upstream_latency_min = DEFAULT_MIN_UPSTREAM_LATENCY; - self->priv->latency = DEFAULT_LATENCY; - self->priv->start_time_selection = DEFAULT_START_TIME_SELECTION; - self->priv->start_time = DEFAULT_START_TIME; - - g_mutex_init (&self->priv->src_lock); - g_cond_init (&self->priv->src_cond); -} - -/* we can't use G_DEFINE_ABSTRACT_TYPE because we need the klass in the _init - * method to get to the padtemplates */ -GType -gst_aggregator_get_type (void) -{ - static gsize type = 0; - - if (g_once_init_enter (&type)) { - GType _type; - static const GTypeInfo info = { - sizeof (GstAggregatorClass), - NULL, - NULL, - (GClassInitFunc) gst_aggregator_class_init, - NULL, - NULL, - sizeof (GstAggregator), - 0, - (GInstanceInitFunc) gst_aggregator_init, - }; - - _type = g_type_register_static (GST_TYPE_ELEMENT, - "GstAggregator", &info, G_TYPE_FLAG_ABSTRACT); - - aggregator_private_offset = - g_type_add_instance_private (_type, sizeof (GstAggregatorPrivate)); - - g_once_init_leave (&type, _type); - } - return type; -} - -/* Must be called with SRC lock and PAD lock held */ -static gboolean -gst_aggregator_pad_has_space (GstAggregator * self, GstAggregatorPad * aggpad) -{ - guint64 max_time_level; - - /* Empty queue always has space */ - if (aggpad->priv->num_buffers == 0 && aggpad->priv->clipped_buffer == NULL) - return TRUE; - - /* We also want at least two buffers, one is being processed and one is ready - * for the next iteration when we operate in live mode. */ - if (self->priv->peer_latency_live && aggpad->priv->num_buffers < 2) - return TRUE; - - /* zero latency, if there is a buffer, it's full */ - if (self->priv->latency == 0) - return FALSE; - - /* On top of our latency, we also want to allow buffering up to the - * minimum upstream latency to allow queue free sources with lower then - * upstream latency. */ - max_time_level = self->priv->latency + self->priv->upstream_latency_min; - - /* Allow no more buffers than the latency */ - return (aggpad->priv->time_level <= max_time_level); -} - -/* Must be called with the PAD_LOCK held */ -static void -apply_buffer (GstAggregatorPad * aggpad, GstBuffer * buffer, gboolean head) -{ - GstClockTime timestamp; - - if (GST_BUFFER_DTS_IS_VALID (buffer)) - timestamp = GST_BUFFER_DTS (buffer); - else - timestamp = GST_BUFFER_PTS (buffer); - - if (timestamp == GST_CLOCK_TIME_NONE) { - if (head) - timestamp = aggpad->priv->head_position; - else - timestamp = aggpad->priv->tail_position; - } - - /* add duration */ - if (GST_BUFFER_DURATION_IS_VALID (buffer)) - timestamp += GST_BUFFER_DURATION (buffer); - - if (head) - aggpad->priv->head_position = timestamp; - else - aggpad->priv->tail_position = timestamp; - - update_time_level (aggpad, head); -} - -/* - * Can be called either from the sinkpad's chain function or from the srcpad's - * thread in the case of a buffer synthetized from a GAP event. - * Because of this second case, FLUSH_LOCK can't be used here. - */ - -static GstFlowReturn -gst_aggregator_pad_chain_internal (GstAggregator * self, - GstAggregatorPad * aggpad, GstBuffer * buffer, gboolean head) -{ - GstFlowReturn flow_return; - GstClockTime buf_pts; - - PAD_LOCK (aggpad); - flow_return = aggpad->priv->flow_return; - if (flow_return != GST_FLOW_OK) - goto flushing; - - PAD_UNLOCK (aggpad); - - buf_pts = GST_BUFFER_PTS (buffer); - - for (;;) { - SRC_LOCK (self); - GST_OBJECT_LOCK (self); - PAD_LOCK (aggpad); - - if (aggpad->priv->first_buffer) { - self->priv->has_peer_latency = FALSE; - aggpad->priv->first_buffer = FALSE; - } - - if ((gst_aggregator_pad_has_space (self, aggpad) || !head) - && aggpad->priv->flow_return == GST_FLOW_OK) { - if (head) - g_queue_push_head (&aggpad->priv->data, buffer); - else - g_queue_push_tail (&aggpad->priv->data, buffer); - apply_buffer (aggpad, buffer, head); - aggpad->priv->num_buffers++; - buffer = NULL; - SRC_BROADCAST (self); - break; - } - - flow_return = aggpad->priv->flow_return; - if (flow_return != GST_FLOW_OK) { - GST_OBJECT_UNLOCK (self); - SRC_UNLOCK (self); - goto flushing; - } - GST_DEBUG_OBJECT (aggpad, "Waiting for buffer to be consumed"); - GST_OBJECT_UNLOCK (self); - SRC_UNLOCK (self); - PAD_WAIT_EVENT (aggpad); - - PAD_UNLOCK (aggpad); - } - - if (self->priv->first_buffer) { - GstClockTime start_time; - GstAggregatorPad *srcpad = GST_AGGREGATOR_PAD (self->srcpad); - - switch (self->priv->start_time_selection) { - case GST_AGGREGATOR_START_TIME_SELECTION_ZERO: - default: - start_time = 0; - break; - case GST_AGGREGATOR_START_TIME_SELECTION_FIRST: - GST_OBJECT_LOCK (aggpad); - if (aggpad->priv->head_segment.format == GST_FORMAT_TIME) { - start_time = buf_pts; - if (start_time != -1) { - start_time = MAX (start_time, aggpad->priv->head_segment.start); - start_time = - gst_segment_to_running_time (&aggpad->priv->head_segment, - GST_FORMAT_TIME, start_time); - } - } else { - start_time = 0; - GST_WARNING_OBJECT (aggpad, - "Ignoring request of selecting the first start time " - "as the segment is a %s segment instead of a time segment", - gst_format_get_name (aggpad->segment.format)); - } - GST_OBJECT_UNLOCK (aggpad); - break; - case GST_AGGREGATOR_START_TIME_SELECTION_SET: - start_time = self->priv->start_time; - if (start_time == -1) - start_time = 0; - break; - } - - if (start_time != -1) { - if (srcpad->segment.position == -1) - srcpad->segment.position = start_time; - else - srcpad->segment.position = MIN (start_time, srcpad->segment.position); - - GST_DEBUG_OBJECT (self, "Selecting start time %" GST_TIME_FORMAT, - GST_TIME_ARGS (start_time)); - } - } - - PAD_UNLOCK (aggpad); - GST_OBJECT_UNLOCK (self); - SRC_UNLOCK (self); - - GST_DEBUG_OBJECT (aggpad, "Done chaining"); - - return flow_return; - -flushing: - PAD_UNLOCK (aggpad); - - GST_DEBUG_OBJECT (aggpad, "Pad is %s, dropping buffer", - gst_flow_get_name (flow_return)); - if (buffer) - gst_buffer_unref (buffer); - - return flow_return; -} - -static GstFlowReturn -gst_aggregator_pad_chain (GstPad * pad, GstObject * object, GstBuffer * buffer) -{ - GstFlowReturn ret; - GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad); - - PAD_FLUSH_LOCK (aggpad); - - ret = gst_aggregator_pad_chain_internal (GST_AGGREGATOR_CAST (object), - aggpad, buffer, TRUE); - - PAD_FLUSH_UNLOCK (aggpad); - - return ret; -} - -static gboolean -gst_aggregator_pad_query_func (GstPad * pad, GstObject * parent, - GstQuery * query) -{ - GstAggregator *self = GST_AGGREGATOR (parent); - GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (self); - GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad); - - g_assert (klass->sink_query_pre_queue); - return klass->sink_query_pre_queue (self, aggpad, query); -} - -static GstFlowReturn -gst_aggregator_pad_event_func (GstPad * pad, GstObject * parent, - GstEvent * event) -{ - GstAggregator *self = GST_AGGREGATOR (parent); - GstAggregatorClass *klass = GST_AGGREGATOR_GET_CLASS (self); - GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad); - - g_assert (klass->sink_event_pre_queue); - return klass->sink_event_pre_queue (self, aggpad, event); -} - -static gboolean -gst_aggregator_pad_activate_mode_func (GstPad * pad, - GstObject * parent, GstPadMode mode, gboolean active) -{ - GstAggregator *self = GST_AGGREGATOR (parent); - GstAggregatorPad *aggpad = GST_AGGREGATOR_PAD (pad); - - if (active == FALSE) { - SRC_LOCK (self); - gst_aggregator_pad_set_flushing (aggpad, GST_FLOW_FLUSHING, TRUE); - SRC_BROADCAST (self); - SRC_UNLOCK (self); - } else { - PAD_LOCK (aggpad); - aggpad->priv->flow_return = GST_FLOW_OK; - PAD_BROADCAST_EVENT (aggpad); - PAD_UNLOCK (aggpad); - } - - return TRUE; -} - -/*********************************** - * GstAggregatorPad implementation * - ************************************/ -G_DEFINE_TYPE_WITH_PRIVATE (GstAggregatorPad, gst_aggregator_pad, GST_TYPE_PAD); - -#define DEFAULT_PAD_EMIT_SIGNALS FALSE - -enum -{ - PAD_PROP_0, - PAD_PROP_EMIT_SIGNALS, -}; - -enum -{ - PAD_SIGNAL_BUFFER_CONSUMED, - PAD_LAST_SIGNAL, -}; - -static guint gst_aggregator_pad_signals[PAD_LAST_SIGNAL] = { 0 }; - -static void -gst_aggregator_pad_constructed (GObject * object) -{ - GstPad *pad = GST_PAD (object); - - if (GST_PAD_IS_SINK (pad)) { - gst_pad_set_chain_function (pad, - GST_DEBUG_FUNCPTR (gst_aggregator_pad_chain)); - gst_pad_set_event_full_function_full (pad, - GST_DEBUG_FUNCPTR (gst_aggregator_pad_event_func), NULL, NULL); - gst_pad_set_query_function (pad, - GST_DEBUG_FUNCPTR (gst_aggregator_pad_query_func)); - gst_pad_set_activatemode_function (pad, - GST_DEBUG_FUNCPTR (gst_aggregator_pad_activate_mode_func)); - } -} - -static void -gst_aggregator_pad_finalize (GObject * object) -{ - GstAggregatorPad *pad = (GstAggregatorPad *) object; - - gst_buffer_replace (&pad->priv->peeked_buffer, NULL); - g_cond_clear (&pad->priv->event_cond); - g_mutex_clear (&pad->priv->flush_lock); - g_mutex_clear (&pad->priv->lock); - - G_OBJECT_CLASS (gst_aggregator_pad_parent_class)->finalize (object); -} - -static void -gst_aggregator_pad_dispose (GObject * object) -{ - GstAggregatorPad *pad = (GstAggregatorPad *) object; - - gst_aggregator_pad_set_flushing (pad, GST_FLOW_FLUSHING, TRUE); - - G_OBJECT_CLASS (gst_aggregator_pad_parent_class)->dispose (object); -} - -static void -gst_aggregator_pad_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstAggregatorPad *pad = GST_AGGREGATOR_PAD (object); - - switch (prop_id) { - case PAD_PROP_EMIT_SIGNALS: - pad->priv->emit_signals = g_value_get_boolean (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_aggregator_pad_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstAggregatorPad *pad = GST_AGGREGATOR_PAD (object); - - switch (prop_id) { - case PAD_PROP_EMIT_SIGNALS: - g_value_set_boolean (value, pad->priv->emit_signals); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_aggregator_pad_class_init (GstAggregatorPadClass * klass) -{ - GObjectClass *gobject_class = (GObjectClass *) klass; - - gobject_class->constructed = gst_aggregator_pad_constructed; - gobject_class->finalize = gst_aggregator_pad_finalize; - gobject_class->dispose = gst_aggregator_pad_dispose; - gobject_class->set_property = gst_aggregator_pad_set_property; - gobject_class->get_property = gst_aggregator_pad_get_property; - - /** - * GstAggregatorPad:buffer-consumed: - * @aggregator: The #GstAggregator that emitted the signal - * @buffer: The buffer that was consumed - * - * Signals that a buffer was consumed. As aggregator pads store buffers - * in an internal queue, there is no direct match between input and output - * buffers at any given time. This signal can be useful to forward metas - * such as #GstVideoTimeCodeMeta or #GstVideoCaptionMeta at the right time. - * - * Since: 1.16 - */ - gst_aggregator_pad_signals[PAD_SIGNAL_BUFFER_CONSUMED] = - g_signal_new ("buffer-consumed", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_BUFFER); - - /** - * GstAggregatorPad:emit-signals: - * - * Enables the emission of signals such as #GstAggregatorPad::buffer-consumed - * - * Since: 1.16 - */ - g_object_class_install_property (gobject_class, PAD_PROP_EMIT_SIGNALS, - g_param_spec_boolean ("emit-signals", "Emit signals", - "Send signals to signal data consumption", DEFAULT_PAD_EMIT_SIGNALS, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -} - -static void -gst_aggregator_pad_init (GstAggregatorPad * pad) -{ - pad->priv = gst_aggregator_pad_get_instance_private (pad); - - g_queue_init (&pad->priv->data); - g_cond_init (&pad->priv->event_cond); - - g_mutex_init (&pad->priv->flush_lock); - g_mutex_init (&pad->priv->lock); - - gst_aggregator_pad_reset_unlocked (pad); - pad->priv->negotiated = FALSE; - pad->priv->emit_signals = DEFAULT_PAD_EMIT_SIGNALS; -} - -/* Must be called with the PAD_LOCK held */ -static void -gst_aggregator_pad_buffer_consumed (GstAggregatorPad * pad, GstBuffer * buffer, - gboolean dequeued) -{ - if (dequeued) - pad->priv->num_buffers--; - - if (buffer && pad->priv->emit_signals) { - g_signal_emit (pad, gst_aggregator_pad_signals[PAD_SIGNAL_BUFFER_CONSUMED], - 0, buffer); - } - PAD_BROADCAST_EVENT (pad); -} - -/* Must be called with the PAD_LOCK held */ -static void -gst_aggregator_pad_clip_buffer_unlocked (GstAggregatorPad * pad) -{ - GstAggregator *self = NULL; - GstAggregatorClass *aggclass = NULL; - GstBuffer *buffer = NULL; - - while (pad->priv->clipped_buffer == NULL && - GST_IS_BUFFER (g_queue_peek_tail (&pad->priv->data))) { - buffer = g_queue_pop_tail (&pad->priv->data); - - apply_buffer (pad, buffer, FALSE); - - /* We only take the parent here so that it's not taken if the buffer is - * already clipped or if the queue is empty. - */ - if (self == NULL) { - self = GST_AGGREGATOR (gst_pad_get_parent_element (GST_PAD (pad))); - if (self == NULL) { - gst_buffer_unref (buffer); - return; - } - - aggclass = GST_AGGREGATOR_GET_CLASS (self); - } - - if (aggclass->clip) { - GST_TRACE_OBJECT (pad, "Clipping: %" GST_PTR_FORMAT, buffer); - - buffer = aggclass->clip (self, pad, buffer); - - if (buffer == NULL) { - gst_aggregator_pad_buffer_consumed (pad, buffer, TRUE); - GST_TRACE_OBJECT (pad, "Clipping consumed the buffer"); - } - } - - pad->priv->clipped_buffer = buffer; - } - - if (self) - gst_object_unref (self); -} - -/** - * gst_aggregator_pad_pop_buffer: - * @pad: the pad to get buffer from - * - * Steal the ref to the buffer currently queued in @pad. - * - * Returns: (nullable) (transfer full): The buffer in @pad or NULL if no buffer was - * queued. You should unref the buffer after usage. - */ -GstBuffer * -gst_aggregator_pad_pop_buffer (GstAggregatorPad * pad) -{ - GstBuffer *buffer = NULL; - - PAD_LOCK (pad); - - /* If the subclass has already peeked a buffer, we guarantee - * that it receives the same buffer, no matter if the pad has - * errored out / been flushed in the meantime. - */ - if (pad->priv->peeked_buffer) { - buffer = pad->priv->peeked_buffer; - goto done; - } - - if (pad->priv->flow_return != GST_FLOW_OK) - goto done; - - gst_aggregator_pad_clip_buffer_unlocked (pad); - buffer = pad->priv->clipped_buffer; - -done: - if (buffer) { - if (pad->priv->clipped_buffer != NULL) { - /* Here we still hold a reference to both the clipped buffer - * and possibly the peeked buffer, we transfer the first and - * potentially release the second - */ - gst_aggregator_pad_buffer_consumed (pad, buffer, TRUE); - pad->priv->clipped_buffer = NULL; - gst_buffer_replace (&pad->priv->peeked_buffer, NULL); - } else { - /* Here our clipped buffer has already been released, for - * example because of a flush. We thus transfer the reference - * to the peeked buffer to the caller, and we don't decrement - * pad.num_buffers as it has already been done elsewhere - */ - gst_aggregator_pad_buffer_consumed (pad, buffer, FALSE); - pad->priv->peeked_buffer = NULL; - } - GST_DEBUG_OBJECT (pad, "Consumed: %" GST_PTR_FORMAT, buffer); - } - - PAD_UNLOCK (pad); - - return buffer; -} - -/** - * gst_aggregator_pad_drop_buffer: - * @pad: the pad where to drop any pending buffer - * - * Drop the buffer currently queued in @pad. - * - * Returns: TRUE if there was a buffer queued in @pad, or FALSE if not. - */ -gboolean -gst_aggregator_pad_drop_buffer (GstAggregatorPad * pad) -{ - GstBuffer *buf; - - buf = gst_aggregator_pad_pop_buffer (pad); - - if (buf == NULL) - return FALSE; - - gst_buffer_unref (buf); - return TRUE; -} - -/** - * gst_aggregator_pad_peek_buffer: - * @pad: the pad to get buffer from - * - * Returns: (nullable) (transfer full): A reference to the buffer in @pad or - * NULL if no buffer was queued. You should unref the buffer after - * usage. - */ -GstBuffer * -gst_aggregator_pad_peek_buffer (GstAggregatorPad * pad) -{ - GstBuffer *buffer = NULL; - - PAD_LOCK (pad); - - if (pad->priv->peeked_buffer) { - buffer = gst_buffer_ref (pad->priv->peeked_buffer); - goto done; - } - - if (pad->priv->flow_return != GST_FLOW_OK) - goto done; - - gst_aggregator_pad_clip_buffer_unlocked (pad); - - if (pad->priv->clipped_buffer) { - buffer = gst_buffer_ref (pad->priv->clipped_buffer); - pad->priv->peeked_buffer = gst_buffer_ref (buffer); - } else { - buffer = NULL; - } - -done: - PAD_UNLOCK (pad); - return buffer; -} - -/** - * gst_aggregator_pad_has_buffer: - * @pad: the pad to check the buffer on - * - * This checks if a pad has a buffer available that will be returned by - * a call to gst_aggregator_pad_peek_buffer() or - * gst_aggregator_pad_pop_buffer(). - * - * Returns: %TRUE if the pad has a buffer available as the next thing. - * - * Since: 1.14.1 - */ -gboolean -gst_aggregator_pad_has_buffer (GstAggregatorPad * pad) -{ - gboolean has_buffer; - - PAD_LOCK (pad); - - if (pad->priv->peeked_buffer) { - has_buffer = TRUE; - } else { - gst_aggregator_pad_clip_buffer_unlocked (pad); - has_buffer = (pad->priv->clipped_buffer != NULL); - if (has_buffer) - pad->priv->peeked_buffer = gst_buffer_ref (pad->priv->clipped_buffer); - } - PAD_UNLOCK (pad); - - return has_buffer; -} - -/** - * gst_aggregator_pad_is_eos: - * @pad: an aggregator pad - * - * Returns: %TRUE if the pad is EOS, otherwise %FALSE. - */ -gboolean -gst_aggregator_pad_is_eos (GstAggregatorPad * pad) -{ - gboolean is_eos; - - PAD_LOCK (pad); - is_eos = pad->priv->eos; - PAD_UNLOCK (pad); - - return is_eos; -} - -#if 0 -/* - * gst_aggregator_merge_tags: - * @self: a #GstAggregator - * @tags: a #GstTagList to merge - * @mode: the #GstTagMergeMode to use - * - * Adds tags to so-called pending tags, which will be processed - * before pushing out data downstream. - * - * Note that this is provided for convenience, and the subclass is - * not required to use this and can still do tag handling on its own. - * - * MT safe. - */ -void -gst_aggregator_merge_tags (GstAggregator * self, - const GstTagList * tags, GstTagMergeMode mode) -{ - GstTagList *otags; - - g_return_if_fail (GST_IS_AGGREGATOR (self)); - g_return_if_fail (tags == NULL || GST_IS_TAG_LIST (tags)); - - /* FIXME Check if we can use OBJECT lock here! */ - GST_OBJECT_LOCK (self); - if (tags) - GST_DEBUG_OBJECT (self, "merging tags %" GST_PTR_FORMAT, tags); - otags = self->priv->tags; - self->priv->tags = gst_tag_list_merge (self->priv->tags, tags, mode); - if (otags) - gst_tag_list_unref (otags); - self->priv->tags_changed = TRUE; - GST_OBJECT_UNLOCK (self); -} -#endif - -/** - * gst_aggregator_set_latency: - * @self: a #GstAggregator - * @min_latency: minimum latency - * @max_latency: maximum latency - * - * Lets #GstAggregator sub-classes tell the baseclass what their internal - * latency is. Will also post a LATENCY message on the bus so the pipeline - * can reconfigure its global latency. - */ -void -gst_aggregator_set_latency (GstAggregator * self, - GstClockTime min_latency, GstClockTime max_latency) -{ - gboolean changed = FALSE; - - g_return_if_fail (GST_IS_AGGREGATOR (self)); - g_return_if_fail (GST_CLOCK_TIME_IS_VALID (min_latency)); - g_return_if_fail (max_latency >= min_latency); - - SRC_LOCK (self); - if (self->priv->sub_latency_min != min_latency) { - self->priv->sub_latency_min = min_latency; - changed = TRUE; - } - if (self->priv->sub_latency_max != max_latency) { - self->priv->sub_latency_max = max_latency; - changed = TRUE; - } - - if (changed) - SRC_BROADCAST (self); - SRC_UNLOCK (self); - - if (changed) { - gst_element_post_message (GST_ELEMENT_CAST (self), - gst_message_new_latency (GST_OBJECT_CAST (self))); - } -} - -/** - * gst_aggregator_get_buffer_pool: - * @self: a #GstAggregator - * - * Returns: (transfer full) (nullable): the instance of the #GstBufferPool used - * by @trans; free it after use it - */ -GstBufferPool * -gst_aggregator_get_buffer_pool (GstAggregator * self) -{ - GstBufferPool *pool; - - g_return_val_if_fail (GST_IS_AGGREGATOR (self), NULL); - - GST_OBJECT_LOCK (self); - pool = self->priv->pool; - if (pool) - gst_object_ref (pool); - GST_OBJECT_UNLOCK (self); - - return pool; -} - -/** - * gst_aggregator_get_allocator: - * @self: a #GstAggregator - * @allocator: (out) (optional) (nullable) (transfer full): the #GstAllocator - * used - * @params: (out caller-allocates) (optional): the - * #GstAllocationParams of @allocator - * - * Lets #GstAggregator sub-classes get the memory @allocator - * acquired by the base class and its @params. - * - * Unref the @allocator after use it. - */ -void -gst_aggregator_get_allocator (GstAggregator * self, - GstAllocator ** allocator, GstAllocationParams * params) -{ - g_return_if_fail (GST_IS_AGGREGATOR (self)); - - if (allocator) - *allocator = self->priv->allocator ? - gst_object_ref (self->priv->allocator) : NULL; - - if (params) - *params = self->priv->allocation_params; -} - -/** - * gst_aggregator_simple_get_next_time: - * @self: A #GstAggregator - * - * This is a simple #GstAggregatorClass::get_next_time implementation that - * just looks at the #GstSegment on the srcpad of the aggregator and bases - * the next time on the running time there. - * - * This is the desired behaviour in most cases where you have a live source - * and you have a dead line based aggregator subclass. - * - * Returns: The running time based on the position - * - * Since: 1.16 - */ -GstClockTime -gst_aggregator_simple_get_next_time (GstAggregator * self) -{ - GstClockTime next_time; - GstAggregatorPad *srcpad = GST_AGGREGATOR_PAD (self->srcpad); - GstSegment *segment = &srcpad->segment; - - GST_OBJECT_LOCK (self); - if (segment->position == -1 || segment->position < segment->start) - next_time = segment->start; - else - next_time = segment->position; - - if (segment->stop != -1 && next_time > segment->stop) - next_time = segment->stop; - - next_time = gst_segment_to_running_time (segment, GST_FORMAT_TIME, next_time); - GST_OBJECT_UNLOCK (self); - - return next_time; -} - -/** - * gst_aggregator_update_segment: - * - * Subclasses should use this to update the segment on their - * source pad, instead of directly pushing new segment events - * downstream. - * - * Subclasses MUST call this before gst_aggregator_selected_samples(), - * if it is used at all. - * - * Since: 1.18 - */ -void -gst_aggregator_update_segment (GstAggregator * self, const GstSegment * segment) -{ - g_return_if_fail (GST_IS_AGGREGATOR (self)); - g_return_if_fail (segment != NULL); - - GST_INFO_OBJECT (self, "Updating srcpad segment: %" GST_SEGMENT_FORMAT, - segment); - - GST_OBJECT_LOCK (self); - GST_AGGREGATOR_PAD (self->srcpad)->segment = *segment; - self->priv->send_segment = TRUE; - /* we have a segment from the subclass now and really shouldn't override - * anything in that segment anymore, like the segment.position */ - self->priv->first_buffer = FALSE; - GST_OBJECT_UNLOCK (self); -} - -/** - * gst_aggregator_selected_samples: - * @pts: The presentation timestamp of the next output buffer - * @dts: The decoding timestamp of the next output buffer - * @duration: The duration of the next output buffer - * @info: (nullable): a #GstStructure containing additional information - * - * Subclasses should call this when they have prepared the - * buffers they will aggregate for each of their sink pads, but - * before using any of the properties of the pads that govern - * *how* aggregation should be performed, for example z-index - * for video aggregators. - * - * If gst_aggregator_update_segment() is used by the subclass, - * it MUST be called before gst_aggregator_selected_samples(). - * - * This function MUST only be called from the #GstAggregatorClass::aggregate() - * function. - * - * Since: 1.18 - */ -void -gst_aggregator_selected_samples (GstAggregator * self, - GstClockTime pts, GstClockTime dts, GstClockTime duration, - GstStructure * info) -{ - g_return_if_fail (GST_IS_AGGREGATOR (self)); - - if (self->priv->emit_signals) { - g_signal_emit (self, gst_aggregator_signals[SIGNAL_SAMPLES_SELECTED], 0, - &GST_AGGREGATOR_PAD (self->srcpad)->segment, pts, dts, duration, info); - } - - self->priv->selected_samples_called_or_warned = TRUE; -} diff --git a/libs/gst/base/gstaggregator.h b/libs/gst/base/gstaggregator.h deleted file mode 100644 index 86fc70ff6d..0000000000 --- a/libs/gst/base/gstaggregator.h +++ /dev/null @@ -1,452 +0,0 @@ -/* GStreamer aggregator base class - * Copyright (C) 2014 Mathieu Duponchelle <mathieu.duponchelle@oencreed.com> - * Copyright (C) 2014 Thibault Saunier <tsaunier@gnome.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., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_AGGREGATOR_H__ -#define __GST_AGGREGATOR_H__ - -#include <gst/gst.h> -#include <gst/base/base-prelude.h> - -G_BEGIN_DECLS - -/************************** - * GstAggregator Structs * - *************************/ - -typedef struct _GstAggregator GstAggregator; -typedef struct _GstAggregatorPrivate GstAggregatorPrivate; -typedef struct _GstAggregatorClass GstAggregatorClass; - -/************************ - * GstAggregatorPad API * - ***********************/ - -#define GST_TYPE_AGGREGATOR_PAD (gst_aggregator_pad_get_type()) -#define GST_AGGREGATOR_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AGGREGATOR_PAD, GstAggregatorPad)) -#define GST_AGGREGATOR_PAD_CAST(obj) ((GstAggregatorPad *)(obj)) -#define GST_AGGREGATOR_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AGGREGATOR_PAD, GstAggregatorPadClass)) -#define GST_AGGREGATOR_PAD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_AGGREGATOR_PAD, GstAggregatorPadClass)) -#define GST_IS_AGGREGATOR_PAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AGGREGATOR_PAD)) -#define GST_IS_AGGREGATOR_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AGGREGATOR_PAD)) - -/**************************** - * GstAggregatorPad Structs * - ***************************/ - -typedef struct _GstAggregatorPad GstAggregatorPad; -typedef struct _GstAggregatorPadClass GstAggregatorPadClass; -typedef struct _GstAggregatorPadPrivate GstAggregatorPadPrivate; - -/** - * GstAggregatorPad: - * @segment: last segment received. - * - * The implementation the GstPad to use with #GstAggregator - * - * Since: 1.14 - */ -struct _GstAggregatorPad -{ - GstPad parent; - - /*< public >*/ - /* Protected by the OBJECT_LOCK */ - GstSegment segment; - - /* < private > */ - GstAggregatorPadPrivate * priv; - - gpointer _gst_reserved[GST_PADDING]; -}; - -/** - * GstAggregatorPadClass: - * @flush: Optional - * Called when the pad has received a flush stop, this is the place - * to flush any information specific to the pad, it allows for individual - * pads to be flushed while others might not be. - * @skip_buffer: Optional - * Called before input buffers are queued in the pad, return %TRUE - * if the buffer should be skipped. - * - * Since: 1.14 - */ -struct _GstAggregatorPadClass -{ - GstPadClass parent_class; - - GstFlowReturn (*flush) (GstAggregatorPad * aggpad, GstAggregator * aggregator); - gboolean (*skip_buffer) (GstAggregatorPad * aggpad, GstAggregator * aggregator, GstBuffer * buffer); - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING_LARGE]; -}; - -GST_BASE_API -GType gst_aggregator_pad_get_type (void); - -/**************************** - * GstAggregatorPad methods * - ***************************/ - -GST_BASE_API -GstBuffer * gst_aggregator_pad_pop_buffer (GstAggregatorPad * pad); - -GST_BASE_API -GstBuffer * gst_aggregator_pad_peek_buffer (GstAggregatorPad * pad); - -GST_BASE_API -gboolean gst_aggregator_pad_drop_buffer (GstAggregatorPad * pad); - -GST_BASE_API -gboolean gst_aggregator_pad_has_buffer (GstAggregatorPad * pad); - -GST_BASE_API -gboolean gst_aggregator_pad_is_eos (GstAggregatorPad * pad); - -/********************* - * GstAggregator API * - ********************/ - -#define GST_TYPE_AGGREGATOR (gst_aggregator_get_type()) -#define GST_AGGREGATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AGGREGATOR,GstAggregator)) -#define GST_AGGREGATOR_CAST(obj) ((GstAggregator *)(obj)) -#define GST_AGGREGATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AGGREGATOR,GstAggregatorClass)) -#define GST_AGGREGATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_AGGREGATOR,GstAggregatorClass)) -#define GST_IS_AGGREGATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AGGREGATOR)) -#define GST_IS_AGGREGATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AGGREGATOR)) - -#define GST_AGGREGATOR_FLOW_NEED_DATA GST_FLOW_CUSTOM_ERROR - -/** - * GstAggregator: - * @srcpad: the aggregator's source pad - * - * Aggregator base class object structure. - * - * Since: 1.14 - */ -struct _GstAggregator -{ - GstElement parent; - - /*< public >*/ - GstPad * srcpad; - - /*< private >*/ - GstAggregatorPrivate * priv; - - gpointer _gst_reserved[GST_PADDING_LARGE]; -}; - -/** - * GstAggregatorClass: - * @flush: Optional. - * Called after a successful flushing seek, once all the flush - * stops have been received. Flush pad-specific data in - * #GstAggregatorPad->flush. - * @clip: Optional. - * Called when a buffer is received on a sink pad, the task of - * clipping it and translating it to the current segment falls - * on the subclass. The function should use the segment of data - * and the negotiated media type on the pad to perform - * clipping of input buffer. This function takes ownership of - * buf and should output a buffer or return NULL in - * if the buffer should be dropped. - * @finish_buffer: Optional. - * Called when a subclass calls gst_aggregator_finish_buffer() - * from their aggregate function to push out a buffer. - * Subclasses can override this to modify or decorate buffers - * before they get pushed out. This function takes ownership - * of the buffer passed. Subclasses that override this method - * should always chain up to the parent class virtual method. - * @sink_event: Optional. - * Called when an event is received on a sink pad, the subclass - * should always chain up. - * @sink_query: Optional. - * Called when a query is received on a sink pad, the subclass - * should always chain up. - * @src_event: Optional. - * Called when an event is received on the src pad, the subclass - * should always chain up. - * @src_query: Optional. - * Called when a query is received on the src pad, the subclass - * should always chain up. - * @src_activate: Optional. - * Called when the src pad is activated, it will start/stop its - * pad task right after that call. - * @aggregate: Mandatory. - * Called when buffers are queued on all sinkpads. Classes - * should iterate the GstElement->sinkpads and peek or steal - * buffers from the #GstAggregatorPads. If the subclass returns - * GST_FLOW_EOS, sending of the eos event will be taken care - * of. Once / if a buffer has been constructed from the - * aggregated buffers, the subclass should call _finish_buffer. - * @stop: Optional. - * Called when the element goes from PAUSED to READY. - * The subclass should free all resources and reset its state. - * @start: Optional. - * Called when the element goes from READY to PAUSED. - * The subclass should get ready to process - * aggregated buffers. - * @get_next_time: Optional. - * Called when the element needs to know the running time of the next - * rendered buffer for live pipelines. This causes deadline - * based aggregation to occur. Defaults to returning - * GST_CLOCK_TIME_NONE causing the element to wait for buffers - * on all sink pads before aggregating. - * @create_new_pad: Optional. - * Called when a new pad needs to be created. Allows subclass that - * don't have a single sink pad template to provide a pad based - * on the provided information. - * @update_src_caps: Lets subclasses update the #GstCaps representing - * the src pad caps before usage. The result should end up - * in @ret. Return %GST_AGGREGATOR_FLOW_NEED_DATA to indicate that the - * element needs more information (caps, a buffer, etc) to - * choose the correct caps. Should return ANY caps if the - * stream has not caps at all. - * @fixate_src_caps: Optional. - * Fixate and return the src pad caps provided. The function takes - * ownership of @caps and returns a fixated version of - * @caps. @caps is not guaranteed to be writable. - * @negotiated_src_caps: Optional. - * Notifies subclasses what caps format has been negotiated - * @decide_allocation: Optional. - * Allows the subclass to influence the allocation choices. - * Setup the allocation parameters for allocating output - * buffers. The passed in query contains the result of the - * downstream allocation query. - * @propose_allocation: Optional. - * Allows the subclass to handle the allocation query from upstream. - * @negotiate: Optional. - * Negotiate the caps with the peer (Since: 1.18). - * @sink_event_pre_queue: Optional. - * Called when an event is received on a sink pad before queueing up - * serialized events. The subclass should always chain up (Since: 1.18). - * @sink_query_pre_queue: Optional. - * Called when a query is received on a sink pad before queueing up - * serialized queries. The subclass should always chain up (Since: 1.18). - * - * The aggregator base class will handle in a thread-safe way all manners of - * concurrent flushes, seeks, pad additions and removals, leaving to the - * subclass the responsibility of clipping buffers, and aggregating buffers in - * the way the implementor sees fit. - * - * It will also take care of event ordering (stream-start, segment, eos). - * - * Basically, a simple implementation will override @aggregate, and call - * _finish_buffer from inside that function. - * - * Since: 1.14 - */ -struct _GstAggregatorClass { - GstElementClass parent_class; - - GstFlowReturn (*flush) (GstAggregator * aggregator); - - GstBuffer * (*clip) (GstAggregator * aggregator, - GstAggregatorPad * aggregator_pad, - GstBuffer * buf); - - GstFlowReturn (*finish_buffer) (GstAggregator * aggregator, - GstBuffer * buffer); - - /* sinkpads virtual methods */ - gboolean (*sink_event) (GstAggregator * aggregator, - GstAggregatorPad * aggregator_pad, - GstEvent * event); - - gboolean (*sink_query) (GstAggregator * aggregator, - GstAggregatorPad * aggregator_pad, - GstQuery * query); - - /* srcpad virtual methods */ - gboolean (*src_event) (GstAggregator * aggregator, - GstEvent * event); - - gboolean (*src_query) (GstAggregator * aggregator, - GstQuery * query); - - gboolean (*src_activate) (GstAggregator * aggregator, - GstPadMode mode, - gboolean active); - - GstFlowReturn (*aggregate) (GstAggregator * aggregator, - gboolean timeout); - - gboolean (*stop) (GstAggregator * aggregator); - - gboolean (*start) (GstAggregator * aggregator); - - GstClockTime (*get_next_time) (GstAggregator * aggregator); - - GstAggregatorPad * (*create_new_pad) (GstAggregator * self, - GstPadTemplate * templ, - const gchar * req_name, - const GstCaps * caps); - - /** - * GstAggregatorClass::update_src_caps: - * @ret: (out) (allow-none): - */ - GstFlowReturn (*update_src_caps) (GstAggregator * self, - GstCaps * caps, - GstCaps ** ret); - GstCaps * (*fixate_src_caps) (GstAggregator * self, - GstCaps * caps); - gboolean (*negotiated_src_caps) (GstAggregator * self, - GstCaps * caps); - gboolean (*decide_allocation) (GstAggregator * self, - GstQuery * query); - gboolean (*propose_allocation) (GstAggregator * self, - GstAggregatorPad * pad, - GstQuery * decide_query, - GstQuery * query); - - gboolean (*negotiate) (GstAggregator * self); - - GstFlowReturn (*sink_event_pre_queue) (GstAggregator * aggregator, - GstAggregatorPad * aggregator_pad, - GstEvent * event); - - gboolean (*sink_query_pre_queue) (GstAggregator * aggregator, - GstAggregatorPad * aggregator_pad, - GstQuery * query); - - /** - * GstAggregatorClass::finish_buffer_list: - * - * Optional. Equivalent of #GstAggregatorClass::finish_buffer for - * buffer lists. - * - * Since: 1.18 - */ - GstFlowReturn (*finish_buffer_list) (GstAggregator * aggregator, - GstBufferList * bufferlist); - /** - * GstAggregatorClass::peek_next_sample: - * - * See gst_aggregator_peek_next_sample(). - * - * Since: 1.18 - */ - GstSample * (*peek_next_sample) (GstAggregator *aggregator, - GstAggregatorPad * aggregator_pad); - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING_LARGE-5]; -}; - -/************************************ - * GstAggregator convenience macros * - ***********************************/ - -/** - * GST_AGGREGATOR_SRC_PAD: - * @agg: a #GstAggregator - * - * Convenience macro to access the source pad of #GstAggregator - * - * Since: 1.6 - */ -#define GST_AGGREGATOR_SRC_PAD(agg) (((GstAggregator *)(agg))->srcpad) - -/************************* - * GstAggregator methods * - ************************/ - -GST_BASE_API -GstFlowReturn gst_aggregator_finish_buffer (GstAggregator * aggregator, - GstBuffer * buffer); - -GST_BASE_API -GstFlowReturn gst_aggregator_finish_buffer_list (GstAggregator * aggregator, - GstBufferList * bufferlist); - -GST_BASE_API -void gst_aggregator_set_src_caps (GstAggregator * self, - GstCaps * caps); - -GST_BASE_API -gboolean gst_aggregator_negotiate (GstAggregator * self); - -GST_BASE_API -void gst_aggregator_set_latency (GstAggregator * self, - GstClockTime min_latency, - GstClockTime max_latency); - -GST_BASE_API -GType gst_aggregator_get_type(void); - -GST_BASE_API -GstClockTime gst_aggregator_get_latency (GstAggregator * self); - -GST_BASE_API -GstBufferPool * gst_aggregator_get_buffer_pool (GstAggregator * self); - -GST_BASE_API -void gst_aggregator_get_allocator (GstAggregator * self, - GstAllocator ** allocator, - GstAllocationParams * params); - -GST_BASE_API -GstClockTime gst_aggregator_simple_get_next_time (GstAggregator * self); - -GST_BASE_API -void gst_aggregator_update_segment (GstAggregator * self, - const GstSegment * segment); - -GST_BASE_API -GstSample * gst_aggregator_peek_next_sample (GstAggregator *self, - GstAggregatorPad * pad); - -GST_BASE_API -void gst_aggregator_selected_samples (GstAggregator * self, - GstClockTime pts, - GstClockTime dts, - GstClockTime duration, - GstStructure * info); - -/** - * GstAggregatorStartTimeSelection: - * @GST_AGGREGATOR_START_TIME_SELECTION_ZERO: Start at running time 0. - * @GST_AGGREGATOR_START_TIME_SELECTION_FIRST: Start at the running time of - * the first buffer that is received. - * @GST_AGGREGATOR_START_TIME_SELECTION_SET: Start at the running time - * selected by the `start-time` property. - * - * Since: 1.18 - */ -typedef enum -{ - GST_AGGREGATOR_START_TIME_SELECTION_ZERO, - GST_AGGREGATOR_START_TIME_SELECTION_FIRST, - GST_AGGREGATOR_START_TIME_SELECTION_SET -} GstAggregatorStartTimeSelection; - -GST_BASE_API -GType gst_aggregator_start_time_selection_get_type (void); - -G_END_DECLS - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstAggregator, gst_object_unref) -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstAggregatorPad, gst_object_unref) - -#endif /* __GST_AGGREGATOR_H__ */ diff --git a/libs/gst/base/gstbaseparse.c b/libs/gst/base/gstbaseparse.c deleted file mode 100644 index b08a284906..0000000000 --- a/libs/gst/base/gstbaseparse.c +++ /dev/null @@ -1,5039 +0,0 @@ -/* GStreamer - * Copyright (C) 2008 Nokia Corporation. All rights reserved. - * Contact: Stefan Kost <stefan.kost@nokia.com> - * Copyright (C) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>. - * Copyright (C) 2011, Hewlett-Packard Development Company, L.P. - * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>, Collabora Ltd. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * SECTION:gstbaseparse - * @title: GstBaseParse - * @short_description: Base class for stream parsers - * @see_also: #GstBaseTransform - * - * This base class is for parser elements that process data and splits it - * into separate audio/video/whatever frames. - * - * It provides for: - * - * * provides one sink pad and one source pad - * * handles state changes - * * can operate in pull mode or push mode - * * handles seeking in both modes - * * handles events (SEGMENT/EOS/FLUSH) - * * handles queries (POSITION/DURATION/SEEKING/FORMAT/CONVERT) - * * handles flushing - * - * The purpose of this base class is to provide the basic functionality of - * a parser and share a lot of rather complex code. - * - * # Description of the parsing mechanism: - * - * ## Set-up phase - * - * * #GstBaseParse calls #GstBaseParseClass::start to inform subclass - * that data processing is about to start now. - * - * * #GstBaseParse class calls #GstBaseParseClass::set_sink_caps to - * inform the subclass about incoming sinkpad caps. Subclass could - * already set the srcpad caps accordingly, but this might be delayed - * until calling gst_base_parse_finish_frame() with a non-queued frame. - * - * * At least at this point subclass needs to tell the #GstBaseParse class - * how big data chunks it wants to receive (minimum frame size ). It can - * do this with gst_base_parse_set_min_frame_size(). - * - * * #GstBaseParse class sets up appropriate data passing mode (pull/push) - * and starts to process the data. - * - * ## Parsing phase - * - * * #GstBaseParse gathers at least min_frame_size bytes of data either - * by pulling it from upstream or collecting buffers in an internal - * #GstAdapter. - * - * * A buffer of (at least) min_frame_size bytes is passed to subclass - * with #GstBaseParseClass::handle_frame. Subclass checks the contents - * and can optionally return #GST_FLOW_OK along with an amount of data - * to be skipped to find a valid frame (which will result in a - * subsequent DISCONT). If, otherwise, the buffer does not hold a - * complete frame, #GstBaseParseClass::handle_frame can merely return - * and will be called again when additional data is available. In push - * mode this amounts to an additional input buffer (thus minimal - * additional latency), in pull mode this amounts to some arbitrary - * reasonable buffer size increase. - * - * Of course, gst_base_parse_set_min_frame_size() could also be used if - * a very specific known amount of additional data is required. If, - * however, the buffer holds a complete valid frame, it can pass the - * size of this frame to gst_base_parse_finish_frame(). - * - * If acting as a converter, it can also merely indicate consumed input - * data while simultaneously providing custom output data. Note that - * baseclass performs some processing (such as tracking overall consumed - * data rate versus duration) for each finished frame, but other state - * is only updated upon each call to #GstBaseParseClass::handle_frame - * (such as tracking upstream input timestamp). - * - * Subclass is also responsible for setting the buffer metadata - * (e.g. buffer timestamp and duration, or keyframe if applicable). - * (although the latter can also be done by #GstBaseParse if it is - * appropriately configured, see below). Frame is provided with - * timestamp derived from upstream (as much as generally possible), - * duration obtained from configuration (see below), and offset - * if meaningful (in pull mode). - * - * Note that #GstBaseParseClass::handle_frame might receive any small - * amount of input data when leftover data is being drained (e.g. at - * EOS). - * - * * As part of finish frame processing, just prior to actually pushing - * the buffer in question, it is passed to - * #GstBaseParseClass::pre_push_frame which gives subclass yet one last - * chance to examine buffer metadata, or to send some custom (tag) - * events, or to perform custom (segment) filtering. - * - * * During the parsing process #GstBaseParseClass will handle both srcpad - * and sinkpad events. They will be passed to subclass if - * #GstBaseParseClass::sink_event or #GstBaseParseClass::src_event - * implementations have been provided. - * - * ## Shutdown phase - * - * * #GstBaseParse class calls #GstBaseParseClass::stop to inform the - * subclass that data parsing will be stopped. - * - * Subclass is responsible for providing pad template caps for source and - * sink pads. The pads need to be named "sink" and "src". It also needs to - * set the fixed caps on srcpad, when the format is ensured (e.g. when - * base class calls subclass' #GstBaseParseClass::set_sink_caps function). - * - * This base class uses %GST_FORMAT_DEFAULT as a meaning of frames. So, - * subclass conversion routine needs to know that conversion from - * %GST_FORMAT_TIME to %GST_FORMAT_DEFAULT must return the - * frame number that can be found from the given byte position. - * - * #GstBaseParse uses subclasses conversion methods also for seeking (or - * otherwise uses its own default one, see also below). - * - * Subclass @start and @stop functions will be called to inform the beginning - * and end of data processing. - * - * Things that subclass need to take care of: - * - * * Provide pad templates - * * Fixate the source pad caps when appropriate - * * Inform base class how big data chunks should be retrieved. This is - * done with gst_base_parse_set_min_frame_size() function. - * * Examine data chunks passed to subclass with - * #GstBaseParseClass::handle_frame and pass proper frame(s) to - * gst_base_parse_finish_frame(), and setting src pad caps and timestamps - * on frame. - * * Provide conversion functions - * * Update the duration information with gst_base_parse_set_duration() - * * Optionally passthrough using gst_base_parse_set_passthrough() - * * Configure various baseparse parameters using - * gst_base_parse_set_average_bitrate(), gst_base_parse_set_syncable() - * and gst_base_parse_set_frame_rate(). - * - * * In particular, if subclass is unable to determine a duration, but - * parsing (or specs) yields a frames per seconds rate, then this can be - * provided to #GstBaseParse to enable it to cater for buffer time - * metadata (which will be taken from upstream as much as - * possible). Internally keeping track of frame durations and respective - * sizes that have been pushed provides #GstBaseParse with an estimated - * bitrate. A default #GstBaseParseClass::convert (used if not - * overridden) will then use these rates to perform obvious conversions. - * These rates are also used to update (estimated) duration at regular - * frame intervals. - * - */ - -/* TODO: - * - In push mode provide a queue of adapter-"queued" buffers for upstream - * buffer metadata - * - Queue buffers/events until caps are set - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include <stdlib.h> -#include <string.h> - -#include <gst/base/gstadapter.h> - -#include "gstbaseparse.h" - -/* FIXME: get rid of old GstIndex code */ -#include "gstindex.h" -#include "gstindex.c" -#include "gstmemindex.c" - -#define GST_BASE_PARSE_FRAME_PRIVATE_FLAG_NOALLOC (1 << 0) - -#define MIN_FRAMES_TO_POST_BITRATE 10 -#define TARGET_DIFFERENCE (20 * GST_SECOND) -#define MAX_INDEX_ENTRIES 4096 -#define UPDATE_THRESHOLD 2 - -#define ABSDIFF(a,b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a))) - -GST_DEBUG_CATEGORY_STATIC (gst_base_parse_debug); -#define GST_CAT_DEFAULT gst_base_parse_debug - -/* Supported formats */ -static const GstFormat fmtlist[] = { - GST_FORMAT_DEFAULT, - GST_FORMAT_BYTES, - GST_FORMAT_TIME, - GST_FORMAT_UNDEFINED -}; - -struct _GstBaseParsePrivate -{ - GstPadMode pad_mode; - - GstAdapter *adapter; - - gint64 duration; - GstFormat duration_fmt; - gint64 estimated_duration; - gint64 estimated_drift; - - guint min_frame_size; - gboolean disable_passthrough; - gboolean passthrough; - gboolean pts_interpolate; - gboolean infer_ts; - gboolean syncable; - gboolean has_timing_info; - guint fps_num, fps_den; - gint update_interval; - guint bitrate; - guint lead_in, lead_out; - GstClockTime lead_in_ts, lead_out_ts; - GstClockTime min_latency, max_latency; - - gboolean discont; - gboolean flushing; - gboolean drain; - gboolean saw_gaps; - - gint64 offset; - gint64 sync_offset; - GstClockTime next_pts; - GstClockTime next_dts; - GstClockTime prev_pts; - GstClockTime prev_dts; - gboolean prev_dts_from_pts; - GstClockTime frame_duration; - gboolean seen_keyframe; - gboolean is_video; - gint flushed; - - guint64 framecount; - guint64 bytecount; - guint64 data_bytecount; - guint64 acc_duration; - GstClockTime first_frame_pts; - GstClockTime first_frame_dts; - gint64 first_frame_offset; - - gboolean post_min_bitrate; - gboolean post_avg_bitrate; - gboolean post_max_bitrate; - - guint min_bitrate; - guint avg_bitrate; - guint max_bitrate; - guint posted_avg_bitrate; - - /* frames/buffers that are queued and ready to go on OK */ - GQueue queued_frames; - - GstBuffer *cache; - - /* index entry storage, either ours or provided */ - GstIndex *index; - gint index_id; - gboolean own_index; - GMutex index_lock; - - /* seek table entries only maintained if upstream is BYTE seekable */ - gboolean upstream_seekable; - gboolean upstream_has_duration; - gint64 upstream_size; - GstFormat upstream_format; - /* minimum distance between two index entries */ - GstClockTimeDiff idx_interval; - guint64 idx_byte_interval; - /* ts and offset of last entry added */ - GstClockTime index_last_ts; - gint64 index_last_offset; - gboolean index_last_valid; - - /* timestamps currently produced are accurate, e.g. started from 0 onwards */ - gboolean exact_position; - /* seek events are temporarily kept to match them with newsegments */ - GSList *pending_seeks; - - /* reverse playback */ - GSList *buffers_pending; - GSList *buffers_head; - GSList *buffers_queued; - GSList *buffers_send; - GstClockTime last_pts; - GstClockTime last_dts; - gint64 last_offset; - - /* Pending serialized events */ - GList *pending_events; - - /* If baseparse has checked the caps to identify if it is - * handling video or audio */ - gboolean checked_media; - - /* offset of last parsed frame/data */ - gint64 prev_offset; - /* force a new frame, regardless of offset */ - gboolean new_frame; - /* whether we are merely scanning for a frame */ - gboolean scanning; - /* ... and resulting frame, if any */ - GstBaseParseFrame *scanned_frame; - - /* TRUE if we're still detecting the format, i.e. - * if ::detect() is still called for future buffers */ - gboolean detecting; - GList *detect_buffers; - guint detect_buffers_size; - - /* True when no buffers have been received yet */ - gboolean first_buffer; - - /* if TRUE, a STREAM_START event needs to be pushed */ - gboolean push_stream_start; - - /* When we need to skip more data than we have currently */ - guint skip; - - /* Tag handling (stream tags only, global tags are passed through as-is) */ - GstTagList *upstream_tags; - GstTagList *parser_tags; - GstTagMergeMode parser_tags_merge_mode; - gboolean tags_changed; - - /* Current segment seqnum */ - guint32 segment_seqnum; -}; - -typedef struct _GstBaseParseSeek -{ - GstSegment segment; - gboolean accurate; - gint64 offset; - GstClockTime start_ts; -} GstBaseParseSeek; - -#define DEFAULT_DISABLE_PASSTHROUGH FALSE - -enum -{ - PROP_0, - PROP_DISABLE_PASSTHROUGH, - PROP_LAST -}; - -#define GST_BASE_PARSE_INDEX_LOCK(parse) \ - g_mutex_lock (&parse->priv->index_lock); -#define GST_BASE_PARSE_INDEX_UNLOCK(parse) \ - g_mutex_unlock (&parse->priv->index_lock); - -static GstElementClass *parent_class = NULL; -static gint base_parse_private_offset = 0; - -static void gst_base_parse_class_init (GstBaseParseClass * klass); -static void gst_base_parse_init (GstBaseParse * parse, - GstBaseParseClass * klass); - -GType -gst_base_parse_get_type (void) -{ - static gsize base_parse_type = 0; - - if (g_once_init_enter (&base_parse_type)) { - static const GTypeInfo base_parse_info = { - sizeof (GstBaseParseClass), - (GBaseInitFunc) NULL, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) gst_base_parse_class_init, - NULL, - NULL, - sizeof (GstBaseParse), - 0, - (GInstanceInitFunc) gst_base_parse_init, - }; - GType _type; - - _type = g_type_register_static (GST_TYPE_ELEMENT, - "GstBaseParse", &base_parse_info, G_TYPE_FLAG_ABSTRACT); - - base_parse_private_offset = - g_type_add_instance_private (_type, sizeof (GstBaseParsePrivate)); - - g_once_init_leave (&base_parse_type, _type); - } - return (GType) base_parse_type; -} - -static inline GstBaseParsePrivate * -gst_base_parse_get_instance_private (GstBaseParse * self) -{ - return (G_STRUCT_MEMBER_P (self, base_parse_private_offset)); -} - -static void gst_base_parse_finalize (GObject * object); - -static GstStateChangeReturn gst_base_parse_change_state (GstElement * element, - GstStateChange transition); -static void gst_base_parse_reset (GstBaseParse * parse); - -#if 0 -static void gst_base_parse_set_index (GstElement * element, GstIndex * index); -static GstIndex *gst_base_parse_get_index (GstElement * element); -#endif - -static gboolean gst_base_parse_sink_activate (GstPad * sinkpad, - GstObject * parent); -static gboolean gst_base_parse_sink_activate_mode (GstPad * pad, - GstObject * parent, GstPadMode mode, gboolean active); -static gboolean gst_base_parse_handle_seek (GstBaseParse * parse, - GstEvent * event); -static void gst_base_parse_set_upstream_tags (GstBaseParse * parse, - GstTagList * taglist); - -static void gst_base_parse_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_base_parse_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static gboolean gst_base_parse_src_event (GstPad * pad, GstObject * parent, - GstEvent * event); -static gboolean gst_base_parse_src_query (GstPad * pad, GstObject * parent, - GstQuery * query); - -static gboolean gst_base_parse_sink_event (GstPad * pad, GstObject * parent, - GstEvent * event); -static gboolean gst_base_parse_sink_query (GstPad * pad, GstObject * parent, - GstQuery * query); - -static GstFlowReturn gst_base_parse_chain (GstPad * pad, GstObject * parent, - GstBuffer * buffer); -static void gst_base_parse_loop (GstPad * pad); - -static GstFlowReturn gst_base_parse_parse_frame (GstBaseParse * parse, - GstBaseParseFrame * frame); - -static gboolean gst_base_parse_sink_event_default (GstBaseParse * parse, - GstEvent * event); - -static gboolean gst_base_parse_src_event_default (GstBaseParse * parse, - GstEvent * event); - -static gboolean gst_base_parse_sink_query_default (GstBaseParse * parse, - GstQuery * query); -static gboolean gst_base_parse_src_query_default (GstBaseParse * parse, - GstQuery * query); - -static gint64 gst_base_parse_find_offset (GstBaseParse * parse, - GstClockTime time, gboolean before, GstClockTime * _ts); -static GstFlowReturn gst_base_parse_locate_time (GstBaseParse * parse, - GstClockTime * _time, gint64 * _offset); - -static GstFlowReturn gst_base_parse_start_fragment (GstBaseParse * parse); -static GstFlowReturn gst_base_parse_finish_fragment (GstBaseParse * parse, - gboolean prev_head); -static GstFlowReturn gst_base_parse_send_buffers (GstBaseParse * parse); - -static inline GstFlowReturn gst_base_parse_check_sync (GstBaseParse * parse); - -static gboolean gst_base_parse_is_seekable (GstBaseParse * parse); - -static void gst_base_parse_push_pending_events (GstBaseParse * parse); - -static void -gst_base_parse_clear_queues (GstBaseParse * parse) -{ - g_slist_foreach (parse->priv->buffers_queued, (GFunc) gst_buffer_unref, NULL); - g_slist_free (parse->priv->buffers_queued); - parse->priv->buffers_queued = NULL; - g_slist_foreach (parse->priv->buffers_pending, (GFunc) gst_buffer_unref, - NULL); - g_slist_free (parse->priv->buffers_pending); - parse->priv->buffers_pending = NULL; - g_slist_foreach (parse->priv->buffers_head, (GFunc) gst_buffer_unref, NULL); - g_slist_free (parse->priv->buffers_head); - parse->priv->buffers_head = NULL; - g_slist_foreach (parse->priv->buffers_send, (GFunc) gst_buffer_unref, NULL); - g_slist_free (parse->priv->buffers_send); - parse->priv->buffers_send = NULL; - - g_list_foreach (parse->priv->detect_buffers, (GFunc) gst_buffer_unref, NULL); - g_list_free (parse->priv->detect_buffers); - parse->priv->detect_buffers = NULL; - parse->priv->detect_buffers_size = 0; - - g_queue_foreach (&parse->priv->queued_frames, - (GFunc) gst_base_parse_frame_free, NULL); - g_queue_clear (&parse->priv->queued_frames); - - gst_buffer_replace (&parse->priv->cache, NULL); - - g_list_foreach (parse->priv->pending_events, (GFunc) gst_event_unref, NULL); - g_list_free (parse->priv->pending_events); - parse->priv->pending_events = NULL; - - parse->priv->checked_media = FALSE; -} - -static void -gst_base_parse_finalize (GObject * object) -{ - GstBaseParse *parse = GST_BASE_PARSE (object); - - g_object_unref (parse->priv->adapter); - - if (parse->priv->index) { - gst_object_unref (parse->priv->index); - parse->priv->index = NULL; - } - g_mutex_clear (&parse->priv->index_lock); - - gst_base_parse_clear_queues (parse); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_base_parse_class_init (GstBaseParseClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - gobject_class = G_OBJECT_CLASS (klass); - - if (base_parse_private_offset != 0) - g_type_class_adjust_private_offset (klass, &base_parse_private_offset); - - parent_class = g_type_class_peek_parent (klass); - - gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_base_parse_finalize); - gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_base_parse_set_property); - gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_base_parse_get_property); - - /** - * GstBaseParse:disable-passthrough: - * - * If set to %TRUE, baseparse will unconditionally force parsing of the - * incoming data. This can be required in the rare cases where the incoming - * side-data (caps, pts, dts, ...) is not trusted by the user and wants to - * force validation and parsing of the incoming data. - * If set to %FALSE, decision of whether to parse the data or not is up to - * the implementation (standard behaviour). - */ - g_object_class_install_property (gobject_class, PROP_DISABLE_PASSTHROUGH, - g_param_spec_boolean ("disable-passthrough", "Disable passthrough", - "Force processing (disables passthrough)", - DEFAULT_DISABLE_PASSTHROUGH, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - gstelement_class = (GstElementClass *) klass; - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_base_parse_change_state); - -#if 0 - gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_base_parse_set_index); - gstelement_class->get_index = GST_DEBUG_FUNCPTR (gst_base_parse_get_index); -#endif - - /* Default handlers */ - klass->sink_event = gst_base_parse_sink_event_default; - klass->src_event = gst_base_parse_src_event_default; - klass->sink_query = gst_base_parse_sink_query_default; - klass->src_query = gst_base_parse_src_query_default; - klass->convert = gst_base_parse_convert_default; - - GST_DEBUG_CATEGORY_INIT (gst_base_parse_debug, "baseparse", 0, - "baseparse element"); -} - -static void -gst_base_parse_init (GstBaseParse * parse, GstBaseParseClass * bclass) -{ - GstPadTemplate *pad_template; - - GST_DEBUG_OBJECT (parse, "gst_base_parse_init"); - - parse->priv = gst_base_parse_get_instance_private (parse); - - pad_template = - gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "sink"); - g_return_if_fail (pad_template != NULL); - parse->sinkpad = gst_pad_new_from_template (pad_template, "sink"); - gst_pad_set_event_function (parse->sinkpad, - GST_DEBUG_FUNCPTR (gst_base_parse_sink_event)); - gst_pad_set_query_function (parse->sinkpad, - GST_DEBUG_FUNCPTR (gst_base_parse_sink_query)); - gst_pad_set_chain_function (parse->sinkpad, - GST_DEBUG_FUNCPTR (gst_base_parse_chain)); - gst_pad_set_activate_function (parse->sinkpad, - GST_DEBUG_FUNCPTR (gst_base_parse_sink_activate)); - gst_pad_set_activatemode_function (parse->sinkpad, - GST_DEBUG_FUNCPTR (gst_base_parse_sink_activate_mode)); - GST_PAD_SET_PROXY_ALLOCATION (parse->sinkpad); - gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad); - - GST_DEBUG_OBJECT (parse, "sinkpad created"); - - pad_template = - gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "src"); - g_return_if_fail (pad_template != NULL); - parse->srcpad = gst_pad_new_from_template (pad_template, "src"); - gst_pad_set_event_function (parse->srcpad, - GST_DEBUG_FUNCPTR (gst_base_parse_src_event)); - gst_pad_set_query_function (parse->srcpad, - GST_DEBUG_FUNCPTR (gst_base_parse_src_query)); - gst_pad_use_fixed_caps (parse->srcpad); - gst_element_add_pad (GST_ELEMENT (parse), parse->srcpad); - GST_DEBUG_OBJECT (parse, "src created"); - - g_queue_init (&parse->priv->queued_frames); - - parse->priv->adapter = gst_adapter_new (); - - parse->priv->pad_mode = GST_PAD_MODE_NONE; - - g_mutex_init (&parse->priv->index_lock); - - /* init state */ - gst_base_parse_reset (parse); - GST_DEBUG_OBJECT (parse, "init ok"); - - GST_OBJECT_FLAG_SET (parse, GST_ELEMENT_FLAG_INDEXABLE); - - parse->priv->upstream_tags = NULL; - parse->priv->parser_tags = NULL; - parse->priv->parser_tags_merge_mode = GST_TAG_MERGE_APPEND; - parse->priv->disable_passthrough = DEFAULT_DISABLE_PASSTHROUGH; -} - -static void -gst_base_parse_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstBaseParse *parse = GST_BASE_PARSE (object); - - switch (prop_id) { - case PROP_DISABLE_PASSTHROUGH: - parse->priv->disable_passthrough = g_value_get_boolean (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_base_parse_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstBaseParse *parse = GST_BASE_PARSE (object); - - switch (prop_id) { - case PROP_DISABLE_PASSTHROUGH: - g_value_set_boolean (value, parse->priv->disable_passthrough); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/** - * gst_base_parse_frame_copy: - * @frame: a #GstBaseParseFrame - * - * Copies a #GstBaseParseFrame. - * - * Returns: A copy of @frame - * - * Since: 1.12.1 - */ -GstBaseParseFrame * -gst_base_parse_frame_copy (GstBaseParseFrame * frame) -{ - GstBaseParseFrame *copy; - - copy = g_slice_dup (GstBaseParseFrame, frame); - copy->buffer = gst_buffer_ref (frame->buffer); - copy->_private_flags &= ~GST_BASE_PARSE_FRAME_PRIVATE_FLAG_NOALLOC; - - GST_TRACE ("copied frame %p -> %p", frame, copy); - - return copy; -} - -/** - * gst_base_parse_frame_free: - * @frame: A #GstBaseParseFrame - * - * Frees the provided @frame. - */ -void -gst_base_parse_frame_free (GstBaseParseFrame * frame) -{ - GST_TRACE ("freeing frame %p", frame); - - if (frame->buffer) { - gst_buffer_unref (frame->buffer); - frame->buffer = NULL; - } - - if (!(frame->_private_flags & GST_BASE_PARSE_FRAME_PRIVATE_FLAG_NOALLOC)) { - g_slice_free (GstBaseParseFrame, frame); - } else { - memset (frame, 0, sizeof (*frame)); - } -} - -G_DEFINE_BOXED_TYPE (GstBaseParseFrame, gst_base_parse_frame, - (GBoxedCopyFunc) gst_base_parse_frame_copy, - (GBoxedFreeFunc) gst_base_parse_frame_free); - -/** - * gst_base_parse_frame_init: - * @frame: #GstBaseParseFrame. - * - * Sets a #GstBaseParseFrame to initial state. Currently this means - * all public fields are zero-ed and a private flag is set to make - * sure gst_base_parse_frame_free() only frees the contents but not - * the actual frame. Use this function to initialise a #GstBaseParseFrame - * allocated on the stack. - */ -void -gst_base_parse_frame_init (GstBaseParseFrame * frame) -{ - memset (frame, 0, sizeof (GstBaseParseFrame)); - frame->_private_flags = GST_BASE_PARSE_FRAME_PRIVATE_FLAG_NOALLOC; - GST_TRACE ("inited frame %p", frame); -} - -/** - * gst_base_parse_frame_new: - * @buffer: (transfer none): a #GstBuffer - * @flags: the flags - * @overhead: number of bytes in this frame which should be counted as - * metadata overhead, ie. not used to calculate the average bitrate. - * Set to -1 to mark the entire frame as metadata. If in doubt, set to 0. - * - * Allocates a new #GstBaseParseFrame. This function is mainly for bindings, - * elements written in C should usually allocate the frame on the stack and - * then use gst_base_parse_frame_init() to initialise it. - * - * Returns: a newly-allocated #GstBaseParseFrame. Free with - * gst_base_parse_frame_free() when no longer needed. - */ -GstBaseParseFrame * -gst_base_parse_frame_new (GstBuffer * buffer, GstBaseParseFrameFlags flags, - gint overhead) -{ - GstBaseParseFrame *frame; - - frame = g_slice_new0 (GstBaseParseFrame); - frame->buffer = gst_buffer_ref (buffer); - - GST_TRACE ("created frame %p", frame); - return frame; -} - -static inline void -gst_base_parse_update_flags (GstBaseParse * parse) -{ - parse->flags = 0; - - /* set flags one by one for clarity */ - if (G_UNLIKELY (parse->priv->drain)) - parse->flags |= GST_BASE_PARSE_FLAG_DRAINING; - - /* losing sync is pretty much a discont (and vice versa), no ? */ - if (G_UNLIKELY (parse->priv->discont)) - parse->flags |= GST_BASE_PARSE_FLAG_LOST_SYNC; -} - -static inline void -gst_base_parse_update_frame (GstBaseParse * parse, GstBaseParseFrame * frame) -{ - if (G_UNLIKELY (parse->priv->discont)) { - GST_DEBUG_OBJECT (parse, "marking DISCONT"); - GST_BUFFER_FLAG_SET (frame->buffer, GST_BUFFER_FLAG_DISCONT); - } else { - GST_BUFFER_FLAG_UNSET (frame->buffer, GST_BUFFER_FLAG_DISCONT); - } - - if (parse->priv->prev_offset != parse->priv->offset || parse->priv->new_frame) { - GST_LOG_OBJECT (parse, "marking as new frame"); - frame->flags |= GST_BASE_PARSE_FRAME_FLAG_NEW_FRAME; - } - - frame->offset = parse->priv->prev_offset = parse->priv->offset; -} - -static void -gst_base_parse_reset (GstBaseParse * parse) -{ - GST_OBJECT_LOCK (parse); - gst_segment_init (&parse->segment, GST_FORMAT_TIME); - parse->priv->duration = -1; - parse->priv->min_frame_size = 1; - parse->priv->discont = TRUE; - parse->priv->flushing = FALSE; - parse->priv->saw_gaps = FALSE; - parse->priv->offset = 0; - parse->priv->sync_offset = 0; - parse->priv->update_interval = -1; - parse->priv->fps_num = parse->priv->fps_den = 0; - parse->priv->frame_duration = GST_CLOCK_TIME_NONE; - parse->priv->lead_in = parse->priv->lead_out = 0; - parse->priv->lead_in_ts = parse->priv->lead_out_ts = 0; - parse->priv->bitrate = 0; - parse->priv->framecount = 0; - parse->priv->bytecount = 0; - parse->priv->acc_duration = 0; - parse->priv->first_frame_pts = GST_CLOCK_TIME_NONE; - parse->priv->first_frame_dts = GST_CLOCK_TIME_NONE; - parse->priv->first_frame_offset = -1; - parse->priv->estimated_duration = -1; - parse->priv->estimated_drift = 0; - parse->priv->next_pts = GST_CLOCK_TIME_NONE; - parse->priv->next_dts = 0; - parse->priv->syncable = TRUE; - parse->priv->passthrough = FALSE; - parse->priv->pts_interpolate = TRUE; - parse->priv->infer_ts = TRUE; - parse->priv->has_timing_info = FALSE; - parse->priv->min_bitrate = G_MAXUINT; - parse->priv->max_bitrate = 0; - parse->priv->avg_bitrate = 0; - parse->priv->posted_avg_bitrate = 0; - - parse->priv->index_last_ts = GST_CLOCK_TIME_NONE; - parse->priv->index_last_offset = -1; - parse->priv->index_last_valid = TRUE; - parse->priv->upstream_seekable = FALSE; - parse->priv->upstream_size = 0; - parse->priv->upstream_has_duration = FALSE; - parse->priv->upstream_format = GST_FORMAT_UNDEFINED; - parse->priv->idx_interval = 0; - parse->priv->idx_byte_interval = 0; - parse->priv->exact_position = TRUE; - parse->priv->seen_keyframe = FALSE; - parse->priv->checked_media = FALSE; - - parse->priv->last_dts = GST_CLOCK_TIME_NONE; - parse->priv->last_pts = GST_CLOCK_TIME_NONE; - parse->priv->last_offset = 0; - - parse->priv->skip = 0; - - g_list_foreach (parse->priv->pending_events, (GFunc) gst_mini_object_unref, - NULL); - g_list_free (parse->priv->pending_events); - parse->priv->pending_events = NULL; - - if (parse->priv->cache) { - gst_buffer_unref (parse->priv->cache); - parse->priv->cache = NULL; - } - - g_slist_foreach (parse->priv->pending_seeks, (GFunc) g_free, NULL); - g_slist_free (parse->priv->pending_seeks); - parse->priv->pending_seeks = NULL; - - if (parse->priv->adapter) - gst_adapter_clear (parse->priv->adapter); - - gst_base_parse_set_upstream_tags (parse, NULL); - - if (parse->priv->parser_tags) { - gst_tag_list_unref (parse->priv->parser_tags); - parse->priv->parser_tags = NULL; - } - parse->priv->parser_tags_merge_mode = GST_TAG_MERGE_APPEND; - - parse->priv->new_frame = TRUE; - - parse->priv->first_buffer = TRUE; - - g_list_foreach (parse->priv->detect_buffers, (GFunc) gst_buffer_unref, NULL); - g_list_free (parse->priv->detect_buffers); - parse->priv->detect_buffers = NULL; - parse->priv->detect_buffers_size = 0; - - parse->priv->segment_seqnum = GST_SEQNUM_INVALID; - GST_OBJECT_UNLOCK (parse); -} - -static gboolean -gst_base_parse_check_bitrate_tag (GstBaseParse * parse, const gchar * tag) -{ - gboolean got_tag = FALSE; - guint n = 0; - - if (parse->priv->upstream_tags != NULL) - got_tag = gst_tag_list_get_uint (parse->priv->upstream_tags, tag, &n); - - if (!got_tag && parse->priv->parser_tags != NULL) - got_tag = gst_tag_list_get_uint (parse->priv->parser_tags, tag, &n); - - return got_tag; -} - -/* check if upstream or subclass tags contain bitrates already */ -static void -gst_base_parse_check_bitrate_tags (GstBaseParse * parse) -{ - parse->priv->post_min_bitrate = - !gst_base_parse_check_bitrate_tag (parse, GST_TAG_MINIMUM_BITRATE); - parse->priv->post_avg_bitrate = - !gst_base_parse_check_bitrate_tag (parse, GST_TAG_BITRATE); - parse->priv->post_max_bitrate = - !gst_base_parse_check_bitrate_tag (parse, GST_TAG_MAXIMUM_BITRATE); -} - -/* Queues new tag event with the current combined state of the stream tags - * (i.e. upstream tags merged with subclass tags and current baseparse tags) */ -static void -gst_base_parse_queue_tag_event_update (GstBaseParse * parse) -{ - GstTagList *merged_tags; - - GST_LOG_OBJECT (parse, "upstream : %" GST_PTR_FORMAT, - parse->priv->upstream_tags); - GST_LOG_OBJECT (parse, "parser : %" GST_PTR_FORMAT, - parse->priv->parser_tags); - GST_LOG_OBJECT (parse, "mode : %d", parse->priv->parser_tags_merge_mode); - - merged_tags = - gst_tag_list_merge (parse->priv->upstream_tags, parse->priv->parser_tags, - parse->priv->parser_tags_merge_mode); - - GST_DEBUG_OBJECT (parse, "merged : %" GST_PTR_FORMAT, merged_tags); - - if (merged_tags == NULL) - return; - - if (gst_tag_list_is_empty (merged_tags)) { - gst_tag_list_unref (merged_tags); - return; - } - - if (parse->priv->framecount >= MIN_FRAMES_TO_POST_BITRATE) { - /* only add bitrate tags to non-empty taglists for now, and only if neither - * upstream tags nor the subclass sets the bitrate tag in question already */ - if (parse->priv->min_bitrate != G_MAXUINT && parse->priv->post_min_bitrate) { - GST_LOG_OBJECT (parse, "adding min bitrate %u", parse->priv->min_bitrate); - gst_tag_list_add (merged_tags, GST_TAG_MERGE_KEEP, - GST_TAG_MINIMUM_BITRATE, parse->priv->min_bitrate, NULL); - } - if (parse->priv->max_bitrate != 0 && parse->priv->post_max_bitrate) { - GST_LOG_OBJECT (parse, "adding max bitrate %u", parse->priv->max_bitrate); - gst_tag_list_add (merged_tags, GST_TAG_MERGE_KEEP, - GST_TAG_MAXIMUM_BITRATE, parse->priv->max_bitrate, NULL); - } - if (parse->priv->avg_bitrate != 0 && parse->priv->post_avg_bitrate) { - parse->priv->posted_avg_bitrate = parse->priv->avg_bitrate; - GST_LOG_OBJECT (parse, "adding avg bitrate %u", parse->priv->avg_bitrate); - gst_tag_list_add (merged_tags, GST_TAG_MERGE_KEEP, - GST_TAG_BITRATE, parse->priv->avg_bitrate, NULL); - } - } - - parse->priv->pending_events = - g_list_prepend (parse->priv->pending_events, - gst_event_new_tag (merged_tags)); -} - -/* gst_base_parse_parse_frame: - * @parse: #GstBaseParse. - * @buffer: #GstBuffer. - * - * Default callback for parse_frame. - */ -static GstFlowReturn -gst_base_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) -{ - GstBuffer *buffer = frame->buffer; - gboolean must_approximate_pts = !GST_BUFFER_PTS_IS_VALID (buffer) - && GST_CLOCK_TIME_IS_VALID (parse->priv->next_pts); - gboolean must_approximate_dts = !GST_BUFFER_DTS_IS_VALID (buffer) - && GST_CLOCK_TIME_IS_VALID (parse->priv->next_dts); - - if (must_approximate_pts) { - GST_BUFFER_PTS (buffer) = parse->priv->next_pts; - if (!must_approximate_dts - && GST_BUFFER_DTS (buffer) > parse->priv->next_pts - && GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DTS (buffer))) { - /* Can't present a frame before it's decoded: change the pts! This can - * happen, for example, when accumulating rounding errors from the - * buffer durations. Assume DTS is correct because only PTS is - * approximated here */ - GST_LOG_OBJECT (parse, - "Found DTS (%" GST_TIME_FORMAT ") > PTS (%" GST_TIME_FORMAT - "), set PTS = DTS", GST_TIME_ARGS (GST_BUFFER_DTS (buffer)), - GST_TIME_ARGS (GST_BUFFER_PTS (buffer))); - GST_BUFFER_PTS (buffer) = GST_BUFFER_DTS (buffer); - } - } - - if (must_approximate_dts) { - if (!must_approximate_pts - && GST_BUFFER_PTS (buffer) < parse->priv->next_dts) { - /* Can't present a frame before it's decoded: change the dts! This can - * happen, for example, when accumulating rounding errors from the - * buffer durations. Assume PTS is correct because only DTS is - * approximated here */ - GST_LOG_OBJECT (parse, - "Found DTS (%" GST_TIME_FORMAT ") > PTS (%" GST_TIME_FORMAT - "), set DTS = PTS", GST_TIME_ARGS (GST_BUFFER_DTS (buffer)), - GST_TIME_ARGS (GST_BUFFER_PTS (buffer))); - GST_BUFFER_DTS (buffer) = GST_BUFFER_PTS (buffer); - } else { - GST_BUFFER_DTS (buffer) = parse->priv->next_dts; - } - } - - if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_PTS (buffer)) - && GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DTS (buffer)) - && GST_BUFFER_PTS (buffer) < GST_BUFFER_DTS (buffer)) { - /* Can't present a frame before it's decoded: change the pts! This can - * happen, for example, when accumulating rounding errors from the buffer - * durations. PTS and DTS are either both approximated or both from the - * original buffer timestamp. Set PTS = DTS because the opposite has been - * observed to cause DTS going backwards */ - GST_LOG_OBJECT (parse, - "Found DTS (%" GST_TIME_FORMAT ") > PTS (%" GST_TIME_FORMAT - "), set PTS = DTS", GST_TIME_ARGS (GST_BUFFER_DTS (buffer)), - GST_TIME_ARGS (GST_BUFFER_PTS (buffer))); - GST_BUFFER_PTS (buffer) = GST_BUFFER_DTS (buffer); - } - - if (!GST_BUFFER_DURATION_IS_VALID (buffer) && - GST_CLOCK_TIME_IS_VALID (parse->priv->frame_duration)) { - GST_BUFFER_DURATION (buffer) = parse->priv->frame_duration; - } - return GST_FLOW_OK; -} - -/* gst_base_parse_convert: - * @parse: #GstBaseParse. - * @src_format: #GstFormat describing the source format. - * @src_value: Source value to be converted. - * @dest_format: #GstFormat defining the converted format. - * @dest_value: Pointer where the conversion result will be put. - * - * Converts using configured "convert" vmethod in #GstBaseParse class. - * - * Returns: %TRUE if conversion was successful. - */ -static gboolean -gst_base_parse_convert (GstBaseParse * parse, - GstFormat src_format, - gint64 src_value, GstFormat dest_format, gint64 * dest_value) -{ - GstBaseParseClass *klass = GST_BASE_PARSE_GET_CLASS (parse); - gboolean ret; - - g_return_val_if_fail (dest_value != NULL, FALSE); - - if (!klass->convert) - return FALSE; - - ret = klass->convert (parse, src_format, src_value, dest_format, dest_value); - -#ifndef GST_DISABLE_GST_DEBUG - { - if (ret) { - if (src_format == GST_FORMAT_TIME && dest_format == GST_FORMAT_BYTES) { - GST_LOG_OBJECT (parse, - "TIME -> BYTES: %" GST_TIME_FORMAT " -> %" G_GINT64_FORMAT, - GST_TIME_ARGS (src_value), *dest_value); - } else if (dest_format == GST_FORMAT_TIME && - src_format == GST_FORMAT_BYTES) { - GST_LOG_OBJECT (parse, - "BYTES -> TIME: %" G_GINT64_FORMAT " -> %" GST_TIME_FORMAT, - src_value, GST_TIME_ARGS (*dest_value)); - } else { - GST_LOG_OBJECT (parse, - "%s -> %s: %" G_GINT64_FORMAT " -> %" G_GINT64_FORMAT, - GST_STR_NULL (gst_format_get_name (src_format)), - GST_STR_NULL (gst_format_get_name (dest_format)), - src_value, *dest_value); - } - } else { - GST_DEBUG_OBJECT (parse, "conversion failed"); - } - } -#endif - - return ret; -} - -static gboolean -update_upstream_provided (GQuark field_id, const GValue * value, - gpointer user_data) -{ - GstCaps *default_caps = user_data; - gint i; - gint caps_size; - - caps_size = gst_caps_get_size (default_caps); - for (i = 0; i < caps_size; i++) { - GstStructure *structure = gst_caps_get_structure (default_caps, i); - if (!gst_structure_id_has_field (structure, field_id)) { - gst_structure_id_set_value (structure, field_id, value); - } - /* XXX: maybe try to fixate better than gst_caps_fixate() the - * downstream caps based on upstream values if possible */ - } - - return TRUE; -} - -static GstCaps * -gst_base_parse_negotiate_default_caps (GstBaseParse * parse) -{ - GstCaps *caps, *templcaps; - GstCaps *sinkcaps = NULL; - GstCaps *default_caps = NULL; - GstStructure *structure; - - templcaps = gst_pad_get_pad_template_caps (GST_BASE_PARSE_SRC_PAD (parse)); - caps = gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), templcaps); - if (caps) - gst_caps_unref (templcaps); - else - caps = templcaps; - templcaps = NULL; - - if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) { - goto caps_error; - } - - GST_LOG_OBJECT (parse, "peer caps %" GST_PTR_FORMAT, caps); - - /* before fixating, try to use whatever upstream provided */ - default_caps = gst_caps_copy (caps); - sinkcaps = gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (parse)); - - GST_LOG_OBJECT (parse, "current caps %" GST_PTR_FORMAT " for sinkpad", - sinkcaps); - - if (sinkcaps) { - structure = gst_caps_get_structure (sinkcaps, 0); - gst_structure_foreach (structure, update_upstream_provided, default_caps); - } - - default_caps = gst_caps_fixate (default_caps); - - if (!default_caps) { - GST_WARNING_OBJECT (parse, "Failed to create default caps !"); - goto caps_error; - } - - GST_INFO_OBJECT (parse, - "Chose default caps %" GST_PTR_FORMAT " for initial gap", default_caps); - - if (sinkcaps) - gst_caps_unref (sinkcaps); - gst_caps_unref (caps); - - return default_caps; - -caps_error: - { - if (caps) - gst_caps_unref (caps); - if (sinkcaps) - gst_caps_unref (sinkcaps); - return NULL; - } -} - -/* gst_base_parse_sink_event: - * @pad: #GstPad that received the event. - * @event: #GstEvent to be handled. - * - * Handler for sink pad events. - * - * Returns: %TRUE if the event was handled. - */ -static gboolean -gst_base_parse_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) -{ - GstBaseParse *parse = GST_BASE_PARSE (parent); - GstBaseParseClass *bclass = GST_BASE_PARSE_GET_CLASS (parse); - gboolean ret; - - ret = bclass->sink_event (parse, event); - - return ret; -} - -/* gst_base_parse_sink_event_default: - * @parse: #GstBaseParse. - * @event: #GstEvent to be handled. - * - * Element-level event handler function. - * - * The event will be unreffed only if it has been handled and this - * function returns %TRUE - * - * Returns: %TRUE if the event was handled and not need forwarding. - */ -static gboolean -gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event) -{ - GstBaseParseClass *klass = GST_BASE_PARSE_GET_CLASS (parse); - gboolean ret = FALSE; - gboolean forward_immediate = FALSE; - - GST_DEBUG_OBJECT (parse, "handling event %d, %s", GST_EVENT_TYPE (event), - GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_CAPS: - { - GstCaps *caps; - - gst_event_parse_caps (event, &caps); - GST_DEBUG_OBJECT (parse, "caps: %" GST_PTR_FORMAT, caps); - - if (klass->set_sink_caps) - ret = klass->set_sink_caps (parse, caps); - else - ret = TRUE; - - /* will send our own caps downstream */ - gst_event_unref (event); - event = NULL; - break; - } - case GST_EVENT_SEGMENT: - { - const GstSegment *in_segment; - GstSegment out_segment; - gint64 offset = 0, next_dts; - - parse->priv->segment_seqnum = gst_event_get_seqnum (event); - gst_event_parse_segment (event, &in_segment); - gst_segment_init (&out_segment, GST_FORMAT_TIME); - out_segment.rate = in_segment->rate; - out_segment.applied_rate = in_segment->applied_rate; - - GST_DEBUG_OBJECT (parse, "New segment %" GST_SEGMENT_FORMAT, in_segment); - GST_DEBUG_OBJECT (parse, "Current segment %" GST_SEGMENT_FORMAT, - &parse->segment); - - parse->priv->upstream_format = in_segment->format; - if (in_segment->format == GST_FORMAT_BYTES) { - GstBaseParseSeek *seek = NULL; - GSList *node; - - /* stop time is allowed to be open-ended, but not start & pos */ - offset = in_segment->time; - - GST_OBJECT_LOCK (parse); - for (node = parse->priv->pending_seeks; node; node = node->next) { - GstBaseParseSeek *tmp = node->data; - - if (tmp->offset == offset) { - seek = tmp; - break; - } - } - parse->priv->pending_seeks = - g_slist_remove (parse->priv->pending_seeks, seek); - GST_OBJECT_UNLOCK (parse); - - if (seek) { - GST_DEBUG_OBJECT (parse, - "Matched newsegment to%s seek: %" GST_SEGMENT_FORMAT, - seek->accurate ? " accurate" : "", &seek->segment); - - out_segment.start = seek->segment.start; - out_segment.stop = seek->segment.stop; - out_segment.time = seek->segment.start; - - next_dts = seek->start_ts; - parse->priv->exact_position = seek->accurate; - g_free (seek); - } else { - /* best attempt convert */ - /* as these are only estimates, stop is kept open-ended to avoid - * premature cutting */ - gst_base_parse_convert (parse, GST_FORMAT_BYTES, in_segment->start, - GST_FORMAT_TIME, (gint64 *) & next_dts); - - out_segment.start = next_dts; - out_segment.stop = GST_CLOCK_TIME_NONE; - out_segment.time = next_dts; - - parse->priv->exact_position = (in_segment->start == 0); - } - - gst_event_unref (event); - - event = gst_event_new_segment (&out_segment); - gst_event_set_seqnum (event, parse->priv->segment_seqnum); - - GST_DEBUG_OBJECT (parse, "Converted incoming segment to TIME. %" - GST_SEGMENT_FORMAT, in_segment); - - } else if (in_segment->format != GST_FORMAT_TIME) { - /* Unknown incoming segment format. Output a default open-ended - * TIME segment */ - gst_event_unref (event); - - out_segment.start = 0; - out_segment.stop = GST_CLOCK_TIME_NONE; - out_segment.time = 0; - - event = gst_event_new_segment (&out_segment); - gst_event_set_seqnum (event, parse->priv->segment_seqnum); - - next_dts = 0; - } else { - /* not considered BYTE seekable if it is talking to us in TIME, - * whatever else it might claim */ - parse->priv->upstream_seekable = FALSE; - next_dts = GST_CLOCK_TIME_NONE; - gst_event_copy_segment (event, &out_segment); - } - - GST_DEBUG_OBJECT (parse, "OUT segment %" GST_SEGMENT_FORMAT, - &out_segment); - memcpy (&parse->segment, &out_segment, sizeof (GstSegment)); - - /* - gst_segment_set_newsegment (&parse->segment, update, rate, - applied_rate, format, start, stop, start); - */ - - ret = TRUE; - - /* save the segment for later, right before we push a new buffer so that - * the caps are fixed and the next linked element can receive - * the segment but finish the current segment */ - GST_DEBUG_OBJECT (parse, "draining current segment"); - if (in_segment->rate > 0.0) - gst_base_parse_drain (parse); - else - gst_base_parse_finish_fragment (parse, FALSE); - gst_adapter_clear (parse->priv->adapter); - - parse->priv->offset = offset; - parse->priv->sync_offset = offset; - parse->priv->next_dts = next_dts; - parse->priv->next_pts = GST_CLOCK_TIME_NONE; - parse->priv->last_pts = GST_CLOCK_TIME_NONE; - parse->priv->last_dts = GST_CLOCK_TIME_NONE; - parse->priv->prev_pts = GST_CLOCK_TIME_NONE; - parse->priv->prev_dts = GST_CLOCK_TIME_NONE; - parse->priv->prev_dts_from_pts = FALSE; - parse->priv->discont = TRUE; - parse->priv->seen_keyframe = FALSE; - parse->priv->skip = 0; - break; - } - - case GST_EVENT_SEGMENT_DONE: - /* need to drain now, rather than upon a new segment, - * since that would have SEGMENT_DONE come before potential - * delayed last part of the current segment */ - GST_DEBUG_OBJECT (parse, "draining current segment"); - if (parse->segment.rate > 0.0) - gst_base_parse_drain (parse); - else - gst_base_parse_finish_fragment (parse, FALSE); - /* Also forward event immediately, there might be no new data - * coming afterwards that would allow us to forward it later */ - forward_immediate = TRUE; - break; - - case GST_EVENT_FLUSH_START: - GST_OBJECT_LOCK (parse); - parse->priv->flushing = TRUE; - GST_OBJECT_UNLOCK (parse); - break; - - case GST_EVENT_FLUSH_STOP: - gst_adapter_clear (parse->priv->adapter); - gst_base_parse_clear_queues (parse); - parse->priv->flushing = FALSE; - parse->priv->discont = TRUE; - parse->priv->last_pts = GST_CLOCK_TIME_NONE; - parse->priv->last_dts = GST_CLOCK_TIME_NONE; - parse->priv->new_frame = TRUE; - parse->priv->skip = 0; - - forward_immediate = TRUE; - break; - - case GST_EVENT_EOS: - if (parse->segment.rate > 0.0) - gst_base_parse_drain (parse); - else - gst_base_parse_finish_fragment (parse, TRUE); - - /* If we STILL have zero frames processed, fire an error */ - if (parse->priv->framecount == 0 && !parse->priv->saw_gaps && - !parse->priv->first_buffer) { - GST_ELEMENT_ERROR (parse, STREAM, WRONG_TYPE, - ("No valid frames found before end of stream"), (NULL)); - } - - if (!parse->priv->saw_gaps - && parse->priv->framecount < MIN_FRAMES_TO_POST_BITRATE) { - /* We've not posted bitrate tags yet - do so now */ - gst_base_parse_queue_tag_event_update (parse); - } - - /* newsegment and other serialized events before eos */ - gst_base_parse_push_pending_events (parse); - - forward_immediate = TRUE; - break; - case GST_EVENT_CUSTOM_DOWNSTREAM:{ - /* FIXME: Code duplicated from libgstvideo because core can't depend on -base */ -#ifndef GST_VIDEO_EVENT_STILL_STATE_NAME -#define GST_VIDEO_EVENT_STILL_STATE_NAME "GstEventStillFrame" -#endif - - const GstStructure *s; - gboolean ev_still_state; - - s = gst_event_get_structure (event); - if (s != NULL && - gst_structure_has_name (s, GST_VIDEO_EVENT_STILL_STATE_NAME) && - gst_structure_get_boolean (s, "still-state", &ev_still_state)) { - if (ev_still_state) { - GST_DEBUG_OBJECT (parse, "draining current data for still-frame"); - if (parse->segment.rate > 0.0) - gst_base_parse_drain (parse); - else - gst_base_parse_finish_fragment (parse, TRUE); - } - forward_immediate = TRUE; - } - break; - } - case GST_EVENT_GAP: - { - GST_DEBUG_OBJECT (parse, "draining current data due to gap event"); - - /* Ensure we have caps before forwarding the event */ - if (!gst_pad_has_current_caps (GST_BASE_PARSE_SRC_PAD (parse))) { - GstCaps *default_caps = NULL; - if ((default_caps = gst_base_parse_negotiate_default_caps (parse))) { - GList *l; - GstEvent *caps_event = gst_event_new_caps (default_caps); - - GST_DEBUG_OBJECT (parse, - "Store caps event to pending list for initial pre-rolling"); - - /* Events are in decreasing order. Go down the list until we - * find the first pre-CAPS event and insert our CAPS event there. - * - * There should be a SEGMENT event already, which is > CAPS */ - for (l = parse->priv->pending_events; l; l = l->next) { - GstEvent *e = l->data; - - if (GST_EVENT_TYPE (e) < GST_EVENT_CAPS) { - parse->priv->pending_events = - g_list_insert_before (parse->priv->pending_events, l, - caps_event); - break; - } - } - /* No pending event that is < CAPS, so we have to add it at the very - * end of the list */ - if (!l) { - parse->priv->pending_events = - g_list_append (parse->priv->pending_events, caps_event); - } - gst_caps_unref (default_caps); - } else { - gst_event_unref (event); - event = NULL; - ret = FALSE; - GST_ELEMENT_ERROR (parse, STREAM, FORMAT, (NULL), - ("Parser output not negotiated before GAP event.")); - break; - } - } - - gst_base_parse_push_pending_events (parse); - - if (parse->segment.rate > 0.0) - gst_base_parse_drain (parse); - else - gst_base_parse_finish_fragment (parse, TRUE); - forward_immediate = TRUE; - parse->priv->saw_gaps = TRUE; - break; - } - case GST_EVENT_TAG: - { - GstTagList *tags = NULL; - - gst_event_parse_tag (event, &tags); - - /* We only care about stream tags here, global tags we just forward */ - if (gst_tag_list_get_scope (tags) != GST_TAG_SCOPE_STREAM) - break; - - gst_base_parse_set_upstream_tags (parse, tags); - gst_base_parse_queue_tag_event_update (parse); - parse->priv->tags_changed = FALSE; - gst_event_unref (event); - event = NULL; - ret = TRUE; - break; - } - case GST_EVENT_STREAM_START: - { - if (parse->priv->pad_mode != GST_PAD_MODE_PULL) - forward_immediate = TRUE; - - gst_base_parse_set_upstream_tags (parse, NULL); - parse->priv->tags_changed = TRUE; - break; - } - default: - break; - } - - /* Forward non-serialized events and EOS/FLUSH_STOP immediately. - * For EOS this is required because no buffer or serialized event - * will come after EOS and nothing could trigger another - * _finish_frame() call. * - * If the subclass handles sending of EOS manually it can return - * _DROPPED from ::finish() and all other subclasses should have - * decoded/flushed all remaining data before this - * - * For FLUSH_STOP this is required because it is expected - * to be forwarded immediately and no buffers are queued anyway. - */ - if (event) { - if (!GST_EVENT_IS_SERIALIZED (event) || forward_immediate) { - ret = gst_pad_push_event (parse->srcpad, event); - } else { - parse->priv->pending_events = - g_list_prepend (parse->priv->pending_events, event); - ret = TRUE; - } - } - - GST_DEBUG_OBJECT (parse, "event handled"); - - return ret; -} - -static gboolean -gst_base_parse_sink_query_default (GstBaseParse * parse, GstQuery * query) -{ - GstPad *pad; - gboolean res; - - pad = GST_BASE_PARSE_SINK_PAD (parse); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_CAPS: - { - GstBaseParseClass *bclass; - - bclass = GST_BASE_PARSE_GET_CLASS (parse); - - if (bclass->get_sink_caps) { - GstCaps *caps, *filter; - - gst_query_parse_caps (query, &filter); - caps = bclass->get_sink_caps (parse, filter); - GST_LOG_OBJECT (parse, "sink getcaps returning caps %" GST_PTR_FORMAT, - caps); - gst_query_set_caps_result (query, caps); - gst_caps_unref (caps); - - res = TRUE; - } else { - GstCaps *caps, *template_caps, *filter; - - gst_query_parse_caps (query, &filter); - template_caps = gst_pad_get_pad_template_caps (pad); - if (filter != NULL) { - caps = - gst_caps_intersect_full (filter, template_caps, - GST_CAPS_INTERSECT_FIRST); - gst_caps_unref (template_caps); - } else { - caps = template_caps; - } - gst_query_set_caps_result (query, caps); - gst_caps_unref (caps); - - res = TRUE; - } - break; - } - default: - { - res = gst_pad_query_default (pad, GST_OBJECT_CAST (parse), query); - break; - } - } - - return res; -} - -static gboolean -gst_base_parse_sink_query (GstPad * pad, GstObject * parent, GstQuery * query) -{ - GstBaseParseClass *bclass; - GstBaseParse *parse; - gboolean ret; - - parse = GST_BASE_PARSE (parent); - bclass = GST_BASE_PARSE_GET_CLASS (parse); - - GST_DEBUG_OBJECT (parse, "%s query", GST_QUERY_TYPE_NAME (query)); - - if (bclass->sink_query) - ret = bclass->sink_query (parse, query); - else - ret = FALSE; - - GST_LOG_OBJECT (parse, "%s query result: %d %" GST_PTR_FORMAT, - GST_QUERY_TYPE_NAME (query), ret, query); - - return ret; -} - -static gboolean -gst_base_parse_src_query (GstPad * pad, GstObject * parent, GstQuery * query) -{ - GstBaseParseClass *bclass; - GstBaseParse *parse; - gboolean ret; - - parse = GST_BASE_PARSE (parent); - bclass = GST_BASE_PARSE_GET_CLASS (parse); - - GST_DEBUG_OBJECT (parse, "%s query: %" GST_PTR_FORMAT, - GST_QUERY_TYPE_NAME (query), query); - - if (bclass->src_query) - ret = bclass->src_query (parse, query); - else - ret = FALSE; - - GST_LOG_OBJECT (parse, "%s query result: %d %" GST_PTR_FORMAT, - GST_QUERY_TYPE_NAME (query), ret, query); - - return ret; -} - -/* gst_base_parse_src_event: - * @pad: #GstPad that received the event. - * @event: #GstEvent that was received. - * - * Handler for source pad events. - * - * Returns: %TRUE if the event was handled. - */ -static gboolean -gst_base_parse_src_event (GstPad * pad, GstObject * parent, GstEvent * event) -{ - GstBaseParse *parse; - GstBaseParseClass *bclass; - gboolean ret = TRUE; - - parse = GST_BASE_PARSE (parent); - bclass = GST_BASE_PARSE_GET_CLASS (parse); - - GST_DEBUG_OBJECT (parse, "event %d, %s", GST_EVENT_TYPE (event), - GST_EVENT_TYPE_NAME (event)); - - if (bclass->src_event) - ret = bclass->src_event (parse, event); - else - gst_event_unref (event); - - return ret; -} - -static gboolean -gst_base_parse_is_seekable (GstBaseParse * parse) -{ - /* FIXME: could do more here, e.g. check index or just send data from 0 - * in pull mode and let decoder/sink clip */ - return parse->priv->syncable; -} - -/* gst_base_parse_src_event_default: - * @parse: #GstBaseParse. - * @event: #GstEvent that was received. - * - * Default srcpad event handler. - * - * Returns: %TRUE if the event was handled and can be dropped. - */ -static gboolean -gst_base_parse_src_event_default (GstBaseParse * parse, GstEvent * event) -{ - gboolean res = FALSE; - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - if (gst_base_parse_is_seekable (parse)) - res = gst_base_parse_handle_seek (parse, event); - break; - default: - res = gst_pad_event_default (parse->srcpad, GST_OBJECT_CAST (parse), - event); - break; - } - return res; -} - - -/** - * gst_base_parse_convert_default: - * @parse: #GstBaseParse. - * @src_format: #GstFormat describing the source format. - * @src_value: Source value to be converted. - * @dest_format: #GstFormat defining the converted format. - * @dest_value: (out): Pointer where the conversion result will be put. - * - * Default implementation of #GstBaseParseClass::convert. - * - * Returns: %TRUE if conversion was successful. - */ -gboolean -gst_base_parse_convert_default (GstBaseParse * parse, - GstFormat src_format, - gint64 src_value, GstFormat dest_format, gint64 * dest_value) -{ - gboolean ret = FALSE; - guint64 bytes, duration; - - if (G_UNLIKELY (src_format == dest_format)) { - *dest_value = src_value; - return TRUE; - } - - if (G_UNLIKELY (src_value == -1)) { - *dest_value = -1; - return TRUE; - } - - if (G_UNLIKELY (src_value == 0)) { - *dest_value = 0; - return TRUE; - } - - if (parse->priv->upstream_format != GST_FORMAT_BYTES) { - /* don't do byte format conversions if we're not really parsing - * a raw elementary stream, since we don't really have BYTES - * position / duration info */ - if (src_format == GST_FORMAT_BYTES || dest_format == GST_FORMAT_BYTES) - goto no_slaved_conversions; - } - - /* need at least some frames */ - if (!parse->priv->framecount) - goto no_framecount; - - duration = parse->priv->acc_duration / GST_MSECOND; - bytes = parse->priv->bytecount; - - if (G_UNLIKELY (!duration || !bytes)) - goto no_duration_bytes; - - if (src_format == GST_FORMAT_BYTES) { - if (dest_format == GST_FORMAT_TIME) { - /* BYTES -> TIME conversion */ - GST_DEBUG_OBJECT (parse, "converting bytes -> time"); - *dest_value = gst_util_uint64_scale (src_value, duration, bytes); - *dest_value *= GST_MSECOND; - GST_DEBUG_OBJECT (parse, "conversion result: %" G_GINT64_FORMAT " ms", - *dest_value / GST_MSECOND); - ret = TRUE; - } else { - GST_DEBUG_OBJECT (parse, "converting bytes -> other not implemented"); - } - } else if (src_format == GST_FORMAT_TIME) { - if (dest_format == GST_FORMAT_BYTES) { - GST_DEBUG_OBJECT (parse, "converting time -> bytes"); - *dest_value = gst_util_uint64_scale (src_value / GST_MSECOND, bytes, - duration); - GST_DEBUG_OBJECT (parse, - "time %" G_GINT64_FORMAT " ms in bytes = %" G_GINT64_FORMAT, - src_value / GST_MSECOND, *dest_value); - ret = TRUE; - } else { - GST_DEBUG_OBJECT (parse, "converting time -> other not implemented"); - } - } else if (src_format == GST_FORMAT_DEFAULT) { - /* DEFAULT == frame-based */ - if (dest_format == GST_FORMAT_TIME) { - GST_DEBUG_OBJECT (parse, "converting default -> time"); - if (parse->priv->fps_den) { - *dest_value = gst_util_uint64_scale (src_value, - GST_SECOND * parse->priv->fps_den, parse->priv->fps_num); - ret = TRUE; - } - } else { - GST_DEBUG_OBJECT (parse, "converting default -> other not implemented"); - } - } else { - GST_DEBUG_OBJECT (parse, "conversion not implemented"); - } - return ret; - - /* ERRORS */ -no_framecount: - { - GST_DEBUG_OBJECT (parse, "no framecount"); - return FALSE; - } -no_duration_bytes: - { - GST_DEBUG_OBJECT (parse, "no duration %" G_GUINT64_FORMAT ", bytes %" - G_GUINT64_FORMAT, duration, bytes); - return FALSE; - } -no_slaved_conversions: - { - GST_DEBUG_OBJECT (parse, - "Can't do format conversions when upstream format is not BYTES"); - return FALSE; - } -} - -static void -gst_base_parse_update_duration (GstBaseParse * parse) -{ - gint64 ptot, dest_value; - - if (!gst_pad_peer_query_duration (parse->sinkpad, GST_FORMAT_BYTES, &ptot)) - return; - - if (!gst_base_parse_convert (parse, GST_FORMAT_BYTES, ptot, - GST_FORMAT_TIME, &dest_value)) - return; - - /* inform if duration changed, but try to avoid spamming */ - parse->priv->estimated_drift += dest_value - parse->priv->estimated_duration; - - parse->priv->estimated_duration = dest_value; - GST_LOG_OBJECT (parse, - "updated estimated duration to %" GST_TIME_FORMAT, - GST_TIME_ARGS (dest_value)); - - if (parse->priv->estimated_drift > GST_SECOND || - parse->priv->estimated_drift < -GST_SECOND) { - gst_element_post_message (GST_ELEMENT (parse), - gst_message_new_duration_changed (GST_OBJECT (parse))); - parse->priv->estimated_drift = 0; - } -} - -/* gst_base_parse_update_bitrates: - * @parse: #GstBaseParse. - * @buffer: Current frame as a #GstBuffer - * - * Keeps track of the minimum and maximum bitrates, and also maintains a - * running average bitrate of the stream so far. - */ -static void -gst_base_parse_update_bitrates (GstBaseParse * parse, GstBaseParseFrame * frame) -{ - guint64 data_len, frame_dur; - gint overhead; - guint frame_bitrate; - guint64 frame_bitrate64; - GstBuffer *buffer = frame->buffer; - - overhead = frame->overhead; - if (overhead == -1) - return; - - data_len = gst_buffer_get_size (buffer) - overhead; - parse->priv->data_bytecount += data_len; - - /* duration should be valid by now, - * either set by subclass or maybe based on fps settings */ - if (GST_BUFFER_DURATION_IS_VALID (buffer) && parse->priv->acc_duration != 0) { - guint64 avg_bitrate; - - /* Calculate duration of a frame from buffer properties */ - frame_dur = GST_BUFFER_DURATION (buffer); - avg_bitrate = gst_util_uint64_scale (GST_SECOND, - 8 * parse->priv->data_bytecount, parse->priv->acc_duration); - - if (avg_bitrate > G_MAXUINT) - return; - - parse->priv->avg_bitrate = (guint) avg_bitrate; - } else { - /* No way to figure out frame duration (is this even possible?) */ - return; - } - - /* override if subclass provided bitrate, e.g. metadata based */ - if (parse->priv->bitrate) { - parse->priv->avg_bitrate = parse->priv->bitrate; - /* spread this (confirmed) info ASAP */ - if (parse->priv->post_avg_bitrate && - parse->priv->posted_avg_bitrate != parse->priv->avg_bitrate) - parse->priv->tags_changed = TRUE; - } - - if (!frame_dur) - return; - - frame_bitrate64 = gst_util_uint64_scale (GST_SECOND, 8 * data_len, frame_dur); - - if (frame_bitrate64 > G_MAXUINT) - return; - - frame_bitrate = (guint) frame_bitrate64; - - GST_LOG_OBJECT (parse, "frame bitrate %u, avg bitrate %u", frame_bitrate, - parse->priv->avg_bitrate); - - if (parse->priv->framecount < MIN_FRAMES_TO_POST_BITRATE) - return; - - if (parse->priv->framecount == MIN_FRAMES_TO_POST_BITRATE && - (parse->priv->post_min_bitrate || parse->priv->post_avg_bitrate - || parse->priv->post_max_bitrate)) - parse->priv->tags_changed = TRUE; - - if (G_LIKELY (parse->priv->framecount >= MIN_FRAMES_TO_POST_BITRATE)) { - if (frame_bitrate < parse->priv->min_bitrate) { - parse->priv->min_bitrate = frame_bitrate; - if (parse->priv->post_min_bitrate) - parse->priv->tags_changed = TRUE; - } - - if (frame_bitrate > parse->priv->max_bitrate) { - parse->priv->max_bitrate = frame_bitrate; - if (parse->priv->post_max_bitrate) - parse->priv->tags_changed = TRUE; - } - - /* Only update the tag on a 2% change */ - if (parse->priv->post_avg_bitrate && parse->priv->avg_bitrate) { - guint64 diffprev = gst_util_uint64_scale (100, - ABSDIFF (parse->priv->avg_bitrate, parse->priv->posted_avg_bitrate), - parse->priv->avg_bitrate); - if (diffprev >= UPDATE_THRESHOLD) - parse->priv->tags_changed = TRUE; - } - } -} - -/** - * gst_base_parse_add_index_entry: - * @parse: #GstBaseParse. - * @offset: offset of entry - * @ts: timestamp associated with offset - * @key: whether entry refers to keyframe - * @force: add entry disregarding sanity checks - * - * Adds an entry to the index associating @offset to @ts. It is recommended - * to only add keyframe entries. @force allows to bypass checks, such as - * whether the stream is (upstream) seekable, another entry is already "close" - * to the new entry, etc. - * - * Returns: #gboolean indicating whether entry was added - */ -gboolean -gst_base_parse_add_index_entry (GstBaseParse * parse, guint64 offset, - GstClockTime ts, gboolean key, gboolean force) -{ - gboolean ret = FALSE; - GstIndexAssociation associations[2]; - - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (ts), FALSE); - - GST_LOG_OBJECT (parse, "Adding key=%d index entry %" GST_TIME_FORMAT - " @ offset 0x%08" G_GINT64_MODIFIER "x", key, GST_TIME_ARGS (ts), offset); - - if (G_LIKELY (!force)) { - - if (!parse->priv->upstream_seekable) { - GST_DEBUG_OBJECT (parse, "upstream not seekable; discarding"); - goto exit; - } - - /* FIXME need better helper data structure that handles these issues - * related to ongoing collecting of index entries */ - if (parse->priv->index_last_offset + parse->priv->idx_byte_interval >= - (gint64) offset) { - GST_LOG_OBJECT (parse, - "already have entries up to offset 0x%08" G_GINT64_MODIFIER "x", - parse->priv->index_last_offset + parse->priv->idx_byte_interval); - goto exit; - } - - if (GST_CLOCK_TIME_IS_VALID (parse->priv->index_last_ts) && - GST_CLOCK_DIFF (parse->priv->index_last_ts, ts) < - parse->priv->idx_interval) { - GST_LOG_OBJECT (parse, "entry too close to last time %" GST_TIME_FORMAT, - GST_TIME_ARGS (parse->priv->index_last_ts)); - goto exit; - } - - /* if last is not really the last one */ - if (!parse->priv->index_last_valid) { - GstClockTime prev_ts; - - gst_base_parse_find_offset (parse, ts, TRUE, &prev_ts); - if (GST_CLOCK_DIFF (prev_ts, ts) < parse->priv->idx_interval) { - GST_LOG_OBJECT (parse, - "entry too close to existing entry %" GST_TIME_FORMAT, - GST_TIME_ARGS (prev_ts)); - parse->priv->index_last_offset = offset; - parse->priv->index_last_ts = ts; - goto exit; - } - } - } - - associations[0].format = GST_FORMAT_TIME; - associations[0].value = ts; - associations[1].format = GST_FORMAT_BYTES; - associations[1].value = offset; - - /* index might change on-the-fly, although that would be nutty app ... */ - GST_BASE_PARSE_INDEX_LOCK (parse); - gst_index_add_associationv (parse->priv->index, parse->priv->index_id, - (key) ? GST_INDEX_ASSOCIATION_FLAG_KEY_UNIT : - GST_INDEX_ASSOCIATION_FLAG_DELTA_UNIT, 2, - (const GstIndexAssociation *) &associations); - GST_BASE_PARSE_INDEX_UNLOCK (parse); - - if (key) { - parse->priv->index_last_offset = offset; - parse->priv->index_last_ts = ts; - } - - ret = TRUE; - -exit: - return ret; -} - -/* check for seekable upstream, above and beyond a mere query */ -static void -gst_base_parse_check_seekability (GstBaseParse * parse) -{ - GstQuery *query; - gboolean seekable = FALSE; - gint64 start = -1, stop = -1; - guint idx_interval = 0; - guint64 idx_byte_interval = 0; - - query = gst_query_new_seeking (GST_FORMAT_BYTES); - if (!gst_pad_peer_query (parse->sinkpad, query)) { - GST_DEBUG_OBJECT (parse, "seeking query failed"); - goto done; - } - - gst_query_parse_seeking (query, NULL, &seekable, &start, &stop); - - /* try harder to query upstream size if we didn't get it the first time */ - if (seekable && stop == -1) { - GST_DEBUG_OBJECT (parse, "doing duration query to fix up unset stop"); - gst_pad_peer_query_duration (parse->sinkpad, GST_FORMAT_BYTES, &stop); - } - - /* if upstream doesn't know the size, it's likely that it's not seekable in - * practice even if it technically may be seekable */ - if (seekable && (start != 0 || stop <= start)) { - GST_DEBUG_OBJECT (parse, "seekable but unknown start/stop -> disable"); - seekable = FALSE; - } - - /* let's not put every single frame into our index */ - if (seekable) { - if (stop < 10 * 1024 * 1024) - idx_interval = 100; - else if (stop < 100 * 1024 * 1024) - idx_interval = 500; - else - idx_interval = 1000; - - /* ensure that even for large files (e.g. very long audio files), the index - * stays reasonably-size, with some arbitrary limit to the total number of - * index entries */ - idx_byte_interval = (stop - start) / MAX_INDEX_ENTRIES; - GST_DEBUG_OBJECT (parse, - "Limiting index entries to %d, indexing byte interval %" - G_GUINT64_FORMAT " bytes", MAX_INDEX_ENTRIES, idx_byte_interval); - } - -done: - gst_query_unref (query); - - GST_DEBUG_OBJECT (parse, "seekable: %d (%" G_GUINT64_FORMAT " - %" - G_GUINT64_FORMAT ")", seekable, start, stop); - parse->priv->upstream_seekable = seekable; - parse->priv->upstream_size = seekable ? stop : 0; - - GST_DEBUG_OBJECT (parse, "idx_interval: %ums", idx_interval); - parse->priv->idx_interval = idx_interval * GST_MSECOND; - parse->priv->idx_byte_interval = idx_byte_interval; -} - -/* some misc checks on upstream */ -static void -gst_base_parse_check_upstream (GstBaseParse * parse) -{ - gint64 stop; - - if (gst_pad_peer_query_duration (parse->sinkpad, GST_FORMAT_TIME, &stop)) - if (GST_CLOCK_TIME_IS_VALID (stop) && stop) { - /* upstream has one, accept it also, and no further updates */ - gst_base_parse_set_duration (parse, GST_FORMAT_TIME, stop, 0); - parse->priv->upstream_has_duration = TRUE; - } - - GST_DEBUG_OBJECT (parse, "upstream_has_duration: %d", - parse->priv->upstream_has_duration); -} - -/* checks src caps to determine if dealing with audio or video */ -/* TODO maybe forego automagic stuff and let subclass configure it ? */ -static void -gst_base_parse_check_media (GstBaseParse * parse) -{ - GstCaps *caps; - GstStructure *s; - - caps = gst_pad_get_current_caps (parse->srcpad); - if (G_LIKELY (caps) && (s = gst_caps_get_structure (caps, 0))) { - parse->priv->is_video = - g_str_has_prefix (gst_structure_get_name (s), "video"); - } else { - /* historical default */ - parse->priv->is_video = FALSE; - } - if (caps) - gst_caps_unref (caps); - - parse->priv->checked_media = TRUE; - GST_DEBUG_OBJECT (parse, "media is video: %d", parse->priv->is_video); -} - -/* takes ownership of frame */ -static void -gst_base_parse_queue_frame (GstBaseParse * parse, GstBaseParseFrame * frame) -{ - if (!(frame->_private_flags & GST_BASE_PARSE_FRAME_PRIVATE_FLAG_NOALLOC)) { - /* frame allocated on the heap, we can just take ownership */ - g_queue_push_tail (&parse->priv->queued_frames, frame); - GST_TRACE ("queued frame %p", frame); - } else { - GstBaseParseFrame *copy; - - /* probably allocated on the stack, must make a proper copy */ - copy = gst_base_parse_frame_copy (frame); - g_queue_push_tail (&parse->priv->queued_frames, copy); - GST_TRACE ("queued frame %p (copy of %p)", copy, frame); - gst_base_parse_frame_free (frame); - } -} - -/* makes sure that @buf is properly prepared and decorated for passing - * to baseclass, and an equally setup frame is returned setup with @buf. - * Takes ownership of @buf. */ -static GstBaseParseFrame * -gst_base_parse_prepare_frame (GstBaseParse * parse, GstBuffer * buffer) -{ - GstBaseParseFrame *frame = NULL; - - buffer = gst_buffer_make_writable (buffer); - - GST_LOG_OBJECT (parse, - "preparing frame at offset %" G_GUINT64_FORMAT - " (%#" G_GINT64_MODIFIER "x) of size %" G_GSIZE_FORMAT, - GST_BUFFER_OFFSET (buffer), GST_BUFFER_OFFSET (buffer), - gst_buffer_get_size (buffer)); - - GST_BUFFER_OFFSET (buffer) = parse->priv->offset; - - gst_base_parse_update_flags (parse); - - frame = gst_base_parse_frame_new (buffer, 0, 0); - gst_buffer_unref (buffer); - gst_base_parse_update_frame (parse, frame); - - /* clear flags for next frame */ - parse->priv->discont = FALSE; - parse->priv->new_frame = FALSE; - - /* use default handler to provide initial (upstream) metadata */ - gst_base_parse_parse_frame (parse, frame); - - return frame; -} - -/* Wraps buffer in a frame and dispatches to subclass. - * Also manages data skipping and offset handling (including adapter flushing). - * Takes ownership of @buffer */ -static GstFlowReturn -gst_base_parse_handle_buffer (GstBaseParse * parse, GstBuffer * buffer, - gint * skip, gint * flushed) -{ - GstBaseParseClass *klass = GST_BASE_PARSE_GET_CLASS (parse); - GstBaseParseFrame *frame; - GstFlowReturn ret; - - g_return_val_if_fail (skip != NULL || flushed != NULL, GST_FLOW_ERROR); - - GST_LOG_OBJECT (parse, - "handling buffer of size %" G_GSIZE_FORMAT " with dts %" GST_TIME_FORMAT - ", pts %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT, - gst_buffer_get_size (buffer), GST_TIME_ARGS (GST_BUFFER_DTS (buffer)), - GST_TIME_ARGS (GST_BUFFER_PTS (buffer)), - GST_TIME_ARGS (GST_BUFFER_DURATION (buffer))); - - /* track what is being flushed during this single round of frame processing */ - parse->priv->flushed = 0; - *skip = 0; - - /* make it easy for _finish_frame to pick up input data */ - if (parse->priv->pad_mode == GST_PAD_MODE_PULL) { - gst_buffer_ref (buffer); - gst_adapter_push (parse->priv->adapter, buffer); - } - - frame = gst_base_parse_prepare_frame (parse, buffer); - ret = klass->handle_frame (parse, frame, skip); - - *flushed = parse->priv->flushed; - - GST_LOG_OBJECT (parse, "handle_frame skipped %d, flushed %d", - *skip, *flushed); - - /* subclass can only do one of these, or semantics are too unclear */ - g_assert (*skip == 0 || *flushed == 0); - - /* track skipping */ - if (*skip > 0) { - GstClockTime pts, dts; - GstBuffer *outbuf; - - GST_LOG_OBJECT (parse, "finding sync, skipping %d bytes", *skip); - if (parse->segment.rate < 0.0 && !parse->priv->buffers_queued) { - /* reverse playback, and no frames found yet, so we are skipping - * the leading part of a fragment, which may form the tail of - * fragment coming later, hopefully subclass skips efficiently ... */ - pts = gst_adapter_prev_pts (parse->priv->adapter, NULL); - dts = gst_adapter_prev_dts (parse->priv->adapter, NULL); - outbuf = gst_adapter_take_buffer (parse->priv->adapter, *skip); - outbuf = gst_buffer_make_writable (outbuf); - GST_BUFFER_PTS (outbuf) = pts; - GST_BUFFER_DTS (outbuf) = dts; - parse->priv->buffers_head = - g_slist_prepend (parse->priv->buffers_head, outbuf); - outbuf = NULL; - } else { - /* If we're asked to skip more than is available in the adapter, - we need to remember what we need to skip for next iteration */ - gsize av = gst_adapter_available (parse->priv->adapter); - GST_DEBUG ("Asked to skip %u (%" G_GSIZE_FORMAT " available)", *skip, av); - if (av >= *skip) { - gst_adapter_flush (parse->priv->adapter, *skip); - } else { - GST_DEBUG - ("This is more than available, flushing %" G_GSIZE_FORMAT - ", storing %u to skip", av, (guint) (*skip - av)); - parse->priv->skip = *skip - av; - gst_adapter_flush (parse->priv->adapter, av); - *skip = av; - } - } - if (!parse->priv->discont) - parse->priv->sync_offset = parse->priv->offset; - parse->priv->offset += *skip; - parse->priv->discont = TRUE; - /* check for indefinite skipping */ - if (ret == GST_FLOW_OK) - ret = gst_base_parse_check_sync (parse); - } - - parse->priv->offset += *flushed; - - if (parse->priv->pad_mode == GST_PAD_MODE_PULL) { - gst_adapter_clear (parse->priv->adapter); - } - - if (*skip == 0 && *flushed == 0) { - /* Carry over discont if we need more data */ - if (GST_BUFFER_IS_DISCONT (frame->buffer)) - parse->priv->discont = TRUE; - } - - gst_base_parse_frame_free (frame); - - return ret; -} - -/* gst_base_parse_push_pending_events: - * @parse: #GstBaseParse - * - * Pushes the pending events - */ -static void -gst_base_parse_push_pending_events (GstBaseParse * parse) -{ - if (G_UNLIKELY (parse->priv->pending_events)) { - GList *r = g_list_reverse (parse->priv->pending_events); - GList *l; - - parse->priv->pending_events = NULL; - for (l = r; l != NULL; l = l->next) { - gst_pad_push_event (parse->srcpad, GST_EVENT_CAST (l->data)); - } - g_list_free (r); - } -} - -/* gst_base_parse_handle_and_push_frame: - * @parse: #GstBaseParse. - * @klass: #GstBaseParseClass. - * @frame: (transfer full): a #GstBaseParseFrame - * - * Parses the frame from given buffer and pushes it forward. Also performs - * timestamp handling and checks the segment limits. - * - * This is called with srcpad STREAM_LOCK held. - * - * Returns: #GstFlowReturn - */ -static GstFlowReturn -gst_base_parse_handle_and_push_frame (GstBaseParse * parse, - GstBaseParseFrame * frame) -{ - gint64 offset; - GstBuffer *buffer; - - g_return_val_if_fail (frame != NULL, GST_FLOW_ERROR); - - buffer = frame->buffer; - offset = frame->offset; - - /* check if subclass/format can provide ts. - * If so, that allows and enables extra seek and duration determining options */ - if (G_UNLIKELY (parse->priv->first_frame_offset < 0)) { - if (GST_BUFFER_PTS_IS_VALID (buffer) && parse->priv->has_timing_info - && parse->priv->pad_mode == GST_PAD_MODE_PULL) { - parse->priv->first_frame_offset = offset; - parse->priv->first_frame_pts = GST_BUFFER_PTS (buffer); - parse->priv->first_frame_dts = GST_BUFFER_DTS (buffer); - GST_DEBUG_OBJECT (parse, "subclass provided dts %" GST_TIME_FORMAT - ", pts %" GST_TIME_FORMAT " for first frame at offset %" - G_GINT64_FORMAT, GST_TIME_ARGS (parse->priv->first_frame_dts), - GST_TIME_ARGS (parse->priv->first_frame_pts), - parse->priv->first_frame_offset); - if (!GST_CLOCK_TIME_IS_VALID (parse->priv->duration)) { - gint64 off; - GstClockTime last_ts = G_MAXINT64; - - GST_DEBUG_OBJECT (parse, "no duration; trying scan to determine"); - gst_base_parse_locate_time (parse, &last_ts, &off); - if (GST_CLOCK_TIME_IS_VALID (last_ts)) - gst_base_parse_set_duration (parse, GST_FORMAT_TIME, last_ts, 0); - } - } else { - /* disable further checks */ - parse->priv->first_frame_offset = 0; - } - } - - /* track upstream time if provided, not subclass' internal notion of it */ - if (parse->priv->upstream_format == GST_FORMAT_TIME) { - GST_BUFFER_PTS (frame->buffer) = GST_CLOCK_TIME_NONE; - GST_BUFFER_DTS (frame->buffer) = GST_CLOCK_TIME_NONE; - } - - /* interpolating and no valid pts yet, - * start with dts and carry on from there */ - if (parse->priv->infer_ts && parse->priv->pts_interpolate - && !GST_CLOCK_TIME_IS_VALID (parse->priv->next_pts)) - parse->priv->next_pts = parse->priv->next_dts; - - /* again use default handler to add missing metadata; - * we may have new information on frame properties */ - gst_base_parse_parse_frame (parse, frame); - - parse->priv->next_pts = GST_CLOCK_TIME_NONE; - if (GST_BUFFER_DTS_IS_VALID (buffer) && GST_BUFFER_DURATION_IS_VALID (buffer)) { - parse->priv->next_dts = - GST_BUFFER_DTS (buffer) + GST_BUFFER_DURATION (buffer); - if (parse->priv->pts_interpolate && GST_BUFFER_PTS_IS_VALID (buffer)) { - GstClockTime next_pts = - GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer); - if (next_pts >= parse->priv->next_dts) - parse->priv->next_pts = next_pts; - } - } else { - /* we lost track, do not produce bogus time next time around - * (probably means parser subclass has given up on parsing as well) */ - GST_DEBUG_OBJECT (parse, "no next fallback timestamp"); - parse->priv->next_dts = GST_CLOCK_TIME_NONE; - } - - if (parse->priv->upstream_seekable && parse->priv->exact_position && - GST_BUFFER_PTS_IS_VALID (buffer)) - gst_base_parse_add_index_entry (parse, offset, - GST_BUFFER_PTS (buffer), - !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT), FALSE); - - /* All OK, push queued frames if there are any */ - if (G_UNLIKELY (!g_queue_is_empty (&parse->priv->queued_frames))) { - GstBaseParseFrame *queued_frame; - - while ((queued_frame = g_queue_pop_head (&parse->priv->queued_frames))) { - gst_base_parse_push_frame (parse, queued_frame); - gst_base_parse_frame_free (queued_frame); - } - } - - return gst_base_parse_push_frame (parse, frame); -} - -/** - * gst_base_parse_push_frame: - * @parse: #GstBaseParse. - * @frame: (transfer none): a #GstBaseParseFrame - * - * Pushes the frame's buffer downstream, sends any pending events and - * does some timestamp and segment handling. Takes ownership of - * frame's buffer, though caller retains ownership of @frame. - * - * This must be called with sinkpad STREAM_LOCK held. - * - * Returns: #GstFlowReturn - */ -GstFlowReturn -gst_base_parse_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) -{ - GstFlowReturn ret = GST_FLOW_OK; - GstClockTime last_start = GST_CLOCK_TIME_NONE; - GstClockTime last_stop = GST_CLOCK_TIME_NONE; - GstBaseParseClass *klass = GST_BASE_PARSE_GET_CLASS (parse); - GstBuffer *buffer; - gsize size; - - g_return_val_if_fail (frame != NULL, GST_FLOW_ERROR); - g_return_val_if_fail (frame->buffer != NULL, GST_FLOW_ERROR); - - GST_TRACE_OBJECT (parse, "pushing frame %p", frame); - - buffer = frame->buffer; - - GST_LOG_OBJECT (parse, - "processing buffer of size %" G_GSIZE_FORMAT " with dts %" GST_TIME_FORMAT - ", pts %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT, - gst_buffer_get_size (buffer), - GST_TIME_ARGS (GST_BUFFER_DTS (buffer)), - GST_TIME_ARGS (GST_BUFFER_PTS (buffer)), - GST_TIME_ARGS (GST_BUFFER_DURATION (buffer))); - - /* update stats */ - parse->priv->bytecount += frame->size; - if (G_LIKELY (!(frame->flags & GST_BASE_PARSE_FRAME_FLAG_NO_FRAME))) { - parse->priv->framecount++; - if (GST_BUFFER_DURATION_IS_VALID (buffer)) { - parse->priv->acc_duration += GST_BUFFER_DURATION (buffer); - } - } - /* 0 means disabled */ - if (parse->priv->update_interval < 0) - parse->priv->update_interval = 50; - else if (parse->priv->update_interval > 0 && - (parse->priv->framecount % parse->priv->update_interval) == 0) - gst_base_parse_update_duration (parse); - - if (GST_BUFFER_PTS_IS_VALID (buffer)) - last_start = last_stop = GST_BUFFER_PTS (buffer); - if (last_start != GST_CLOCK_TIME_NONE - && GST_BUFFER_DURATION_IS_VALID (buffer)) - last_stop = last_start + GST_BUFFER_DURATION (buffer); - - /* should have caps by now */ - if (!gst_pad_has_current_caps (parse->srcpad)) - goto no_caps; - - if (G_UNLIKELY (!parse->priv->checked_media)) { - /* have caps; check identity */ - gst_base_parse_check_media (parse); - } - - if (parse->priv->tags_changed) { - gst_base_parse_queue_tag_event_update (parse); - parse->priv->tags_changed = FALSE; - } - - /* Push pending events, including SEGMENT events */ - gst_base_parse_push_pending_events (parse); - - /* update bitrates and optionally post corresponding tags - * (following newsegment) */ - gst_base_parse_update_bitrates (parse, frame); - - if (klass->pre_push_frame) { - ret = klass->pre_push_frame (parse, frame); - } else { - frame->flags |= GST_BASE_PARSE_FRAME_FLAG_CLIP; - } - - /* Push pending events, if there are any new ones - * like tags added by pre_push_frame */ - if (parse->priv->tags_changed) { - gst_base_parse_queue_tag_event_update (parse); - parse->priv->tags_changed = FALSE; - } - gst_base_parse_push_pending_events (parse); - - /* take final ownership of frame buffer */ - if (frame->out_buffer) { - buffer = frame->out_buffer; - frame->out_buffer = NULL; - gst_buffer_replace (&frame->buffer, NULL); - } else { - buffer = frame->buffer; - frame->buffer = NULL; - } - - /* subclass must play nice */ - g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR); - - size = gst_buffer_get_size (buffer); - - parse->priv->seen_keyframe |= parse->priv->is_video && - !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); - - if (frame->flags & GST_BASE_PARSE_FRAME_FLAG_CLIP) { - if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) && - GST_CLOCK_TIME_IS_VALID (parse->segment.stop) && - GST_BUFFER_TIMESTAMP (buffer) > - parse->segment.stop + parse->priv->lead_out_ts) { - GST_LOG_OBJECT (parse, "Dropped frame, after segment"); - ret = GST_FLOW_EOS; - } else if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer) && - GST_BUFFER_DURATION_IS_VALID (buffer) && - GST_CLOCK_TIME_IS_VALID (parse->segment.start) && - GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) + - parse->priv->lead_in_ts < parse->segment.start) { - if (parse->priv->seen_keyframe) { - GST_LOG_OBJECT (parse, "Frame before segment, after keyframe"); - ret = GST_FLOW_OK; - } else { - GST_LOG_OBJECT (parse, "Dropped frame, before segment"); - ret = GST_BASE_PARSE_FLOW_DROPPED; - } - } else { - ret = GST_FLOW_OK; - } - } - - if (ret == GST_BASE_PARSE_FLOW_DROPPED) { - GST_LOG_OBJECT (parse, "frame (%" G_GSIZE_FORMAT " bytes) dropped", size); - if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) - parse->priv->discont = TRUE; - gst_buffer_unref (buffer); - ret = GST_FLOW_OK; - } else if (ret == GST_FLOW_OK) { - if (parse->segment.rate > 0.0) { - GST_LOG_OBJECT (parse, "pushing frame (%" G_GSIZE_FORMAT " bytes) now..", - size); - ret = gst_pad_push (parse->srcpad, buffer); - GST_LOG_OBJECT (parse, "frame pushed, flow %s", gst_flow_get_name (ret)); - } else if (!parse->priv->disable_passthrough && parse->priv->passthrough) { - - /* in backwards playback mode, if on passthrough we need to push buffers - * directly without accumulating them into the buffers_queued as baseparse - * will never check for a DISCONT while on passthrough and those buffers - * will never be pushed. - * - * also, as we are on reverse playback, it might be possible that - * passthrough might have just been enabled, so make sure to drain the - * buffers_queued list */ - if (G_UNLIKELY (parse->priv->buffers_queued != NULL)) { - gst_base_parse_finish_fragment (parse, TRUE); - ret = gst_base_parse_send_buffers (parse); - } - - if (ret == GST_FLOW_OK) { - GST_LOG_OBJECT (parse, - "pushing frame (%" G_GSIZE_FORMAT " bytes) now..", size); - ret = gst_pad_push (parse->srcpad, buffer); - GST_LOG_OBJECT (parse, "frame pushed, flow %s", - gst_flow_get_name (ret)); - } else { - GST_LOG_OBJECT (parse, - "frame (%" G_GSIZE_FORMAT " bytes) not pushed: %s", size, - gst_flow_get_name (ret)); - gst_buffer_unref (buffer); - } - - } else { - GST_LOG_OBJECT (parse, "frame (%" G_GSIZE_FORMAT " bytes) queued for now", - size); - parse->priv->buffers_queued = - g_slist_prepend (parse->priv->buffers_queued, buffer); - ret = GST_FLOW_OK; - } - } else { - GST_LOG_OBJECT (parse, "frame (%" G_GSIZE_FORMAT " bytes) not pushed: %s", - size, gst_flow_get_name (ret)); - gst_buffer_unref (buffer); - /* if we are not sufficiently in control, let upstream decide on EOS */ - if (ret == GST_FLOW_EOS && !parse->priv->disable_passthrough && - (parse->priv->passthrough || - (parse->priv->pad_mode == GST_PAD_MODE_PUSH && - !parse->priv->upstream_seekable))) - ret = GST_FLOW_OK; - } - - /* Update current running segment position */ - if ((ret == GST_FLOW_OK || ret == GST_FLOW_NOT_LINKED) - && last_stop != GST_CLOCK_TIME_NONE - && parse->segment.position < last_stop) - parse->segment.position = last_stop; - - return ret; - - /* ERRORS */ -no_caps: - { - if (GST_PAD_IS_FLUSHING (parse->srcpad)) - return GST_FLOW_FLUSHING; - - GST_ELEMENT_ERROR (parse, STREAM, DECODE, ("No caps set"), (NULL)); - return GST_FLOW_ERROR; - } -} - -/** - * gst_base_parse_finish_frame: - * @parse: a #GstBaseParse - * @frame: a #GstBaseParseFrame - * @size: consumed input data represented by frame - * - * Collects parsed data and pushes this downstream. - * Source pad caps must be set when this is called. - * - * If @frame's out_buffer is set, that will be used as subsequent frame data. - * Otherwise, @size samples will be taken from the input and used for output, - * and the output's metadata (timestamps etc) will be taken as (optionally) - * set by the subclass on @frame's (input) buffer (which is otherwise - * ignored for any but the above purpose/information). - * - * Note that the latter buffer is invalidated by this call, whereas the - * caller retains ownership of @frame. - * - * Returns: a #GstFlowReturn that should be escalated to caller (of caller) - */ -GstFlowReturn -gst_base_parse_finish_frame (GstBaseParse * parse, GstBaseParseFrame * frame, - gint size) -{ - GstFlowReturn ret = GST_FLOW_OK; - - g_return_val_if_fail (frame != NULL, GST_FLOW_ERROR); - g_return_val_if_fail (frame->buffer != NULL, GST_FLOW_ERROR); - g_return_val_if_fail (size > 0 || frame->out_buffer, GST_FLOW_ERROR); - g_return_val_if_fail (gst_adapter_available (parse->priv->adapter) >= size, - GST_FLOW_ERROR); - - GST_LOG_OBJECT (parse, "finished frame at offset %" G_GUINT64_FORMAT ", " - "flushing size %d", frame->offset, size); - - /* some one-time start-up */ - if (G_UNLIKELY (parse->priv->framecount == 0)) { - gst_base_parse_check_seekability (parse); - gst_base_parse_check_upstream (parse); - } - - parse->priv->flushed += size; - - if (parse->priv->scanning && frame->buffer) { - if (!parse->priv->scanned_frame) { - parse->priv->scanned_frame = gst_base_parse_frame_copy (frame); - } - goto exit; - } - - /* either PUSH or PULL mode arranges for adapter data */ - /* ensure output buffer */ - if (!frame->out_buffer) { - GstBuffer *src, *dest; - - frame->out_buffer = gst_adapter_take_buffer (parse->priv->adapter, size); - dest = frame->out_buffer; - src = frame->buffer; - GST_BUFFER_PTS (dest) = GST_BUFFER_PTS (src); - GST_BUFFER_DTS (dest) = GST_BUFFER_DTS (src); - GST_BUFFER_OFFSET (dest) = GST_BUFFER_OFFSET (src); - GST_BUFFER_DURATION (dest) = GST_BUFFER_DURATION (src); - GST_BUFFER_OFFSET_END (dest) = GST_BUFFER_OFFSET_END (src); - GST_MINI_OBJECT_FLAGS (dest) = GST_MINI_OBJECT_FLAGS (src); - } else { - gst_adapter_flush (parse->priv->adapter, size); - } - - /* use as input for subsequent processing */ - gst_buffer_replace (&frame->buffer, frame->out_buffer); - gst_buffer_unref (frame->out_buffer); - frame->out_buffer = NULL; - - /* mark input size consumed */ - frame->size = size; - - /* subclass might queue frames/data internally if it needs more - * frames to decide on the format, or might request us to queue here. */ - if (frame->flags & GST_BASE_PARSE_FRAME_FLAG_DROP) { - gst_buffer_replace (&frame->buffer, NULL); - goto exit; - } else if (frame->flags & GST_BASE_PARSE_FRAME_FLAG_QUEUE) { - GstBaseParseFrame *copy; - - copy = gst_base_parse_frame_copy (frame); - copy->flags &= ~GST_BASE_PARSE_FRAME_FLAG_QUEUE; - gst_base_parse_queue_frame (parse, copy); - goto exit; - } - - ret = gst_base_parse_handle_and_push_frame (parse, frame); - -exit: - return ret; -} - -/** - * gst_base_parse_drain: - * @parse: a #GstBaseParse - * - * Drains the adapter until it is empty. It decreases the min_frame_size to - * match the current adapter size and calls chain method until the adapter - * is emptied or chain returns with error. - * - * Since: 1.12 - */ -void -gst_base_parse_drain (GstBaseParse * parse) -{ - guint avail; - - GST_DEBUG_OBJECT (parse, "draining"); - parse->priv->drain = TRUE; - - for (;;) { - avail = gst_adapter_available (parse->priv->adapter); - if (!avail) - break; - - if (gst_base_parse_chain (parse->sinkpad, GST_OBJECT_CAST (parse), - NULL) != GST_FLOW_OK) { - break; - } - - /* nothing changed, maybe due to truncated frame; break infinite loop */ - if (avail == gst_adapter_available (parse->priv->adapter)) { - GST_DEBUG_OBJECT (parse, "no change during draining; flushing"); - gst_adapter_clear (parse->priv->adapter); - } - } - - parse->priv->drain = FALSE; -} - -/* gst_base_parse_send_buffers - * - * Sends buffers collected in send_buffers downstream, and ensures that list - * is empty at the end (errors or not). - */ -static GstFlowReturn -gst_base_parse_send_buffers (GstBaseParse * parse) -{ - GSList *send = NULL; - GstBuffer *buf; - GstFlowReturn ret = GST_FLOW_OK; - gboolean first = TRUE; - - send = parse->priv->buffers_send; - - /* send buffers */ - while (send) { - buf = GST_BUFFER_CAST (send->data); - GST_LOG_OBJECT (parse, "pushing buffer %p, dts %" - GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT - ", offset %" G_GINT64_FORMAT, buf, - GST_TIME_ARGS (GST_BUFFER_DTS (buf)), - GST_TIME_ARGS (GST_BUFFER_PTS (buf)), - GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_BUFFER_OFFSET (buf)); - - /* Make sure the first buffer is always DISCONT. If we split - * GOPs inside the parser this is otherwise not guaranteed */ - if (first) { - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); - first = FALSE; - } else { - /* likewise, subsequent buffers should never have DISCONT - * according to the "reverse fragment protocol", or such would - * confuse a downstream decoder - * (could be DISCONT due to aggregating upstream fragments by parsing) */ - GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT); - } - - /* iterate output queue an push downstream */ - ret = gst_pad_push (parse->srcpad, buf); - send = g_slist_delete_link (send, send); - - /* clear any leftover if error */ - if (G_UNLIKELY (ret != GST_FLOW_OK)) { - while (send) { - buf = GST_BUFFER_CAST (send->data); - gst_buffer_unref (buf); - send = g_slist_delete_link (send, send); - } - } - } - - parse->priv->buffers_send = send; - - return ret; -} - -/* gst_base_parse_start_fragment: - * - * Prepares for processing a reverse playback (forward) fragment - * by (re)setting proper state variables. - */ -static GstFlowReturn -gst_base_parse_start_fragment (GstBaseParse * parse) -{ - GST_LOG_OBJECT (parse, "starting fragment"); - - /* invalidate so no fall-back timestamping is performed; - * ok if taken from subclass or upstream */ - parse->priv->next_pts = GST_CLOCK_TIME_NONE; - parse->priv->prev_pts = GST_CLOCK_TIME_NONE; - parse->priv->next_dts = GST_CLOCK_TIME_NONE; - parse->priv->prev_dts = GST_CLOCK_TIME_NONE; - parse->priv->prev_dts_from_pts = FALSE; - /* prevent it hanging around stop all the time */ - parse->segment.position = GST_CLOCK_TIME_NONE; - /* mark next run */ - parse->priv->discont = TRUE; - - /* head of previous fragment is now pending tail of current fragment */ - parse->priv->buffers_pending = parse->priv->buffers_head; - parse->priv->buffers_head = NULL; - - return GST_FLOW_OK; -} - - -/* gst_base_parse_finish_fragment: - * - * Processes a reverse playback (forward) fragment: - * - append head of last fragment that was skipped to current fragment data - * - drain the resulting current fragment data (i.e. repeated chain) - * - add time/duration (if needed) to frames queued by chain - * - push queued data - */ -static GstFlowReturn -gst_base_parse_finish_fragment (GstBaseParse * parse, gboolean prev_head) -{ - GstBuffer *buf; - GstFlowReturn ret = GST_FLOW_OK; - gboolean seen_key = FALSE, seen_delta = FALSE; - - GST_LOG_OBJECT (parse, "finishing fragment"); - - /* restore order */ - parse->priv->buffers_pending = g_slist_reverse (parse->priv->buffers_pending); - while (parse->priv->buffers_pending) { - buf = GST_BUFFER_CAST (parse->priv->buffers_pending->data); - if (prev_head) { - GST_LOG_OBJECT (parse, "adding pending buffer (size %" G_GSIZE_FORMAT ")", - gst_buffer_get_size (buf)); - gst_adapter_push (parse->priv->adapter, buf); - } else { - GST_LOG_OBJECT (parse, "discarding head buffer"); - gst_buffer_unref (buf); - } - parse->priv->buffers_pending = - g_slist_delete_link (parse->priv->buffers_pending, - parse->priv->buffers_pending); - } - - /* chain looks for frames and queues resulting ones (in stead of pushing) */ - /* initial skipped data is added to buffers_pending */ - gst_base_parse_drain (parse); - - if (parse->priv->buffers_send) { - buf = GST_BUFFER_CAST (parse->priv->buffers_send->data); - seen_key |= !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT); - } - - /* add metadata (if needed to queued buffers */ - GST_LOG_OBJECT (parse, "last timestamp: %" GST_TIME_FORMAT, - GST_TIME_ARGS (parse->priv->last_pts)); - while (parse->priv->buffers_queued) { - buf = GST_BUFFER_CAST (parse->priv->buffers_queued->data); - - /* no touching if upstream or parsing provided time */ - if (GST_BUFFER_PTS_IS_VALID (buf)) { - GST_LOG_OBJECT (parse, "buffer has time %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_PTS (buf))); - } else if (GST_BUFFER_DURATION_IS_VALID (buf)) { - if (GST_CLOCK_TIME_IS_VALID (parse->priv->last_pts)) { - if (G_LIKELY (GST_BUFFER_DURATION (buf) <= parse->priv->last_pts)) - parse->priv->last_pts -= GST_BUFFER_DURATION (buf); - else - parse->priv->last_pts = 0; - GST_BUFFER_PTS (buf) = parse->priv->last_pts; - GST_LOG_OBJECT (parse, "applied time %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_PTS (buf))); - } - if (GST_CLOCK_TIME_IS_VALID (parse->priv->last_dts)) { - if (G_LIKELY (GST_BUFFER_DURATION (buf) <= parse->priv->last_dts)) - parse->priv->last_dts -= GST_BUFFER_DURATION (buf); - else - parse->priv->last_dts = 0; - GST_BUFFER_DTS (buf) = parse->priv->last_dts; - GST_LOG_OBJECT (parse, "applied dts %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_DTS (buf))); - } - } else { - /* no idea, very bad */ - GST_WARNING_OBJECT (parse, "could not determine time for buffer"); - } - - parse->priv->last_pts = GST_BUFFER_PTS (buf); - parse->priv->last_dts = GST_BUFFER_DTS (buf); - - /* reverse order for ascending sending */ - /* send downstream at keyframe not preceded by a keyframe - * (e.g. that should identify start of collection of IDR nals) */ - if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT)) { - if (seen_key) { - ret = gst_base_parse_send_buffers (parse); - /* if a problem, throw all to sending */ - if (ret != GST_FLOW_OK) { - parse->priv->buffers_send = - g_slist_reverse (parse->priv->buffers_queued); - parse->priv->buffers_queued = NULL; - break; - } - seen_key = FALSE; - } - seen_delta = TRUE; - } else { - seen_key = TRUE; - } - - parse->priv->buffers_send = - g_slist_prepend (parse->priv->buffers_send, buf); - parse->priv->buffers_queued = - g_slist_delete_link (parse->priv->buffers_queued, - parse->priv->buffers_queued); - } - - /* audio may have all marked as keyframe, so arrange to send here. Also - * we might have ended the loop above on a keyframe, in which case we - * should */ - if (!seen_delta || seen_key) - ret = gst_base_parse_send_buffers (parse); - - /* any trailing unused no longer usable (ideally none) */ - if (G_UNLIKELY (gst_adapter_available (parse->priv->adapter))) { - GST_DEBUG_OBJECT (parse, "discarding %" G_GSIZE_FORMAT " trailing bytes", - gst_adapter_available (parse->priv->adapter)); - gst_adapter_clear (parse->priv->adapter); - } - - return ret; -} - -/* small helper that checks whether we have been trying to resync too long */ -static inline GstFlowReturn -gst_base_parse_check_sync (GstBaseParse * parse) -{ - if (G_UNLIKELY (parse->priv->discont && - parse->priv->offset - parse->priv->sync_offset > 2 * 1024 * 1024)) { - GST_ELEMENT_ERROR (parse, STREAM, DECODE, - ("Failed to parse stream"), (NULL)); - return GST_FLOW_ERROR; - } - - return GST_FLOW_OK; -} - -static GstFlowReturn -gst_base_parse_process_streamheader (GstBaseParse * parse) -{ - GstCaps *caps; - GstStructure *str; - const GValue *value; - GstFlowReturn ret = GST_FLOW_OK; - - caps = gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (parse)); - if (caps == NULL) - goto notfound; - - str = gst_caps_get_structure (caps, 0); - value = gst_structure_get_value (str, "streamheader"); - if (value == NULL) - goto notfound; - - GST_DEBUG_OBJECT (parse, "Found streamheader field on input caps"); - - if (GST_VALUE_HOLDS_ARRAY (value)) { - gint i; - gsize len = gst_value_array_get_size (value); - - for (i = 0; i < len; i++) { - GstBuffer *buffer = - gst_value_get_buffer (gst_value_array_get_value (value, i)); - ret = - gst_base_parse_chain (GST_BASE_PARSE_SINK_PAD (parse), - GST_OBJECT_CAST (parse), gst_buffer_ref (buffer)); - } - - } else if (GST_VALUE_HOLDS_BUFFER (value)) { - GstBuffer *buffer = gst_value_get_buffer (value); - ret = - gst_base_parse_chain (GST_BASE_PARSE_SINK_PAD (parse), - GST_OBJECT_CAST (parse), gst_buffer_ref (buffer)); - } - - gst_caps_unref (caps); - - return ret; - -notfound: - { - if (caps) { - gst_caps_unref (caps); - } - - GST_DEBUG_OBJECT (parse, "No streamheader on caps"); - return GST_FLOW_OK; - } -} - -static GstFlowReturn -gst_base_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) -{ - GstBaseParseClass *bclass; - GstBaseParse *parse; - GstFlowReturn ret = GST_FLOW_OK; - GstFlowReturn old_ret = GST_FLOW_OK; - GstBuffer *tmpbuf = NULL; - guint fsize = 1; - gint skip = -1; - guint min_size, av; - GstClockTime pts, dts; - - parse = GST_BASE_PARSE (parent); - bclass = GST_BASE_PARSE_GET_CLASS (parse); - GST_DEBUG_OBJECT (parent, "chain"); - - /* early out for speed, if we need to skip */ - if (buffer && GST_BUFFER_IS_DISCONT (buffer)) - parse->priv->skip = 0; - if (parse->priv->skip > 0) { - gsize bsize = gst_buffer_get_size (buffer); - GST_DEBUG ("Got %" G_GSIZE_FORMAT " buffer, need to skip %u", bsize, - parse->priv->skip); - if (parse->priv->skip >= bsize) { - parse->priv->skip -= bsize; - GST_DEBUG ("All the buffer is skipped"); - parse->priv->offset += bsize; - parse->priv->sync_offset = parse->priv->offset; - gst_buffer_unref (buffer); - return GST_FLOW_OK; - } - buffer = gst_buffer_make_writable (buffer); - gst_buffer_resize (buffer, parse->priv->skip, bsize - parse->priv->skip); - parse->priv->offset += parse->priv->skip; - GST_DEBUG ("Done skipping, we have %u left on this buffer", - (unsigned) (bsize - parse->priv->skip)); - parse->priv->skip = 0; - parse->priv->discont = TRUE; - } - - if (G_UNLIKELY (parse->priv->first_buffer)) { - parse->priv->first_buffer = FALSE; - if (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_HEADER)) { - /* this stream has no header buffers, check if we just prepend the - * streamheader from caps to the stream */ - GST_DEBUG_OBJECT (parse, "Looking for streamheader field on caps to " - "prepend to the stream"); - gst_base_parse_process_streamheader (parse); - } else { - GST_DEBUG_OBJECT (parse, "Stream has header buffers, not prepending " - "streamheader from caps"); - } - } - - if (parse->priv->detecting) { - GstBuffer *detect_buf; - - if (parse->priv->detect_buffers_size == 0) { - detect_buf = gst_buffer_ref (buffer); - } else { - GList *l; - guint offset = 0; - - detect_buf = gst_buffer_new (); - - for (l = parse->priv->detect_buffers; l; l = l->next) { - gsize tmpsize = gst_buffer_get_size (l->data); - - gst_buffer_copy_into (detect_buf, GST_BUFFER_CAST (l->data), - GST_BUFFER_COPY_MEMORY, offset, tmpsize); - offset += tmpsize; - } - if (buffer) - gst_buffer_copy_into (detect_buf, buffer, GST_BUFFER_COPY_MEMORY, - offset, gst_buffer_get_size (buffer)); - } - - ret = bclass->detect (parse, detect_buf); - gst_buffer_unref (detect_buf); - - if (ret == GST_FLOW_OK) { - GList *l; - - /* Detected something */ - parse->priv->detecting = FALSE; - - for (l = parse->priv->detect_buffers; l; l = l->next) { - if (ret == GST_FLOW_OK && !parse->priv->flushing) - ret = - gst_base_parse_chain (GST_BASE_PARSE_SINK_PAD (parse), - parent, GST_BUFFER_CAST (l->data)); - else - gst_buffer_unref (GST_BUFFER_CAST (l->data)); - } - g_list_free (parse->priv->detect_buffers); - parse->priv->detect_buffers = NULL; - parse->priv->detect_buffers_size = 0; - - if (ret != GST_FLOW_OK) { - return ret; - } - - /* Handle the current buffer */ - } else if (ret == GST_FLOW_NOT_NEGOTIATED) { - /* Still detecting, append buffer or error out if draining */ - - if (parse->priv->drain) { - GST_DEBUG_OBJECT (parse, "Draining but did not detect format yet"); - return GST_FLOW_ERROR; - } else if (parse->priv->flushing) { - g_list_foreach (parse->priv->detect_buffers, (GFunc) gst_buffer_unref, - NULL); - g_list_free (parse->priv->detect_buffers); - parse->priv->detect_buffers = NULL; - parse->priv->detect_buffers_size = 0; - } else { - parse->priv->detect_buffers = - g_list_append (parse->priv->detect_buffers, buffer); - parse->priv->detect_buffers_size += gst_buffer_get_size (buffer); - return GST_FLOW_OK; - } - } else { - /* Something went wrong, subclass responsible for error reporting */ - return ret; - } - - /* And now handle the current buffer if detection worked */ - } - - if (G_LIKELY (buffer)) { - GST_LOG_OBJECT (parse, - "buffer size: %" G_GSIZE_FORMAT ", offset = %" G_GINT64_FORMAT - ", dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT, - gst_buffer_get_size (buffer), GST_BUFFER_OFFSET (buffer), - GST_TIME_ARGS (GST_BUFFER_DTS (buffer)), - GST_TIME_ARGS (GST_BUFFER_PTS (buffer))); - - if (G_UNLIKELY (!parse->priv->disable_passthrough - && parse->priv->passthrough)) { - GstBaseParseFrame frame; - - gst_base_parse_frame_init (&frame); - frame.buffer = gst_buffer_make_writable (buffer); - ret = gst_base_parse_push_frame (parse, &frame); - gst_base_parse_frame_free (&frame); - return ret; - } - if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT))) { - /* upstream feeding us in reverse playback; - * finish previous fragment and start new upon DISCONT */ - if (parse->segment.rate < 0.0) { - GST_DEBUG_OBJECT (parse, "buffer starts new reverse playback fragment"); - ret = gst_base_parse_finish_fragment (parse, TRUE); - gst_base_parse_start_fragment (parse); - } else { - /* discont in the stream, drain and mark discont for next output */ - gst_base_parse_drain (parse); - parse->priv->discont = TRUE; - } - } - gst_adapter_push (parse->priv->adapter, buffer); - } - - /* Parse and push as many frames as possible */ - /* Stop either when adapter is empty or we are flushing */ - while (!parse->priv->flushing) { - gint flush = 0; - gboolean updated_prev_pts = FALSE; - - /* note: if subclass indicates MAX fsize, - * this will not likely be available anyway ... */ - min_size = MAX (parse->priv->min_frame_size, fsize); - av = gst_adapter_available (parse->priv->adapter); - - if (G_UNLIKELY (parse->priv->drain)) { - min_size = av; - GST_DEBUG_OBJECT (parse, "draining, data left: %d", min_size); - if (G_UNLIKELY (!min_size)) { - goto done; - } - } - - /* Collect at least min_frame_size bytes */ - if (av < min_size) { - GST_DEBUG_OBJECT (parse, "not enough data available (only %d bytes)", av); - goto done; - } - - /* move along with upstream timestamp (if any), - * but interpolate in between */ - pts = gst_adapter_prev_pts (parse->priv->adapter, NULL); - dts = gst_adapter_prev_dts (parse->priv->adapter, NULL); - if (GST_CLOCK_TIME_IS_VALID (pts) && (parse->priv->prev_pts != pts)) { - parse->priv->prev_pts = parse->priv->next_pts = pts; - updated_prev_pts = TRUE; - } - - if (GST_CLOCK_TIME_IS_VALID (dts) && (parse->priv->prev_dts != dts)) { - parse->priv->prev_dts = parse->priv->next_dts = dts; - parse->priv->prev_dts_from_pts = FALSE; - } - - /* we can mess with, erm interpolate, timestamps, - * and incoming stuff has PTS but no DTS seen so far, - * then pick up DTS from PTS and hope for the best ... */ - if (parse->priv->infer_ts && - parse->priv->pts_interpolate && - !GST_CLOCK_TIME_IS_VALID (dts) && - (!GST_CLOCK_TIME_IS_VALID (parse->priv->prev_dts) - || (parse->priv->prev_dts_from_pts && updated_prev_pts)) - && GST_CLOCK_TIME_IS_VALID (pts)) { - parse->priv->prev_dts = parse->priv->next_dts = pts; - parse->priv->prev_dts_from_pts = TRUE; - } - - /* always pass all available data */ - tmpbuf = gst_adapter_get_buffer (parse->priv->adapter, av); - - /* already inform subclass what timestamps we have planned, - * at least if provided by time-based upstream */ - if (parse->priv->upstream_format == GST_FORMAT_TIME) { - tmpbuf = gst_buffer_make_writable (tmpbuf); - GST_BUFFER_PTS (tmpbuf) = parse->priv->next_pts; - GST_BUFFER_DTS (tmpbuf) = parse->priv->next_dts; - GST_BUFFER_DURATION (tmpbuf) = GST_CLOCK_TIME_NONE; - } - - /* keep the adapter mapped, so keep track of what has to be flushed */ - ret = gst_base_parse_handle_buffer (parse, tmpbuf, &skip, &flush); - tmpbuf = NULL; - - if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) { - goto done; - } - if (skip == 0 && flush == 0) { - GST_LOG_OBJECT (parse, "nothing skipped and no frames finished, " - "breaking to get more data"); - /* ignore this return as it produced no data */ - ret = old_ret; - goto done; - } - if (old_ret == GST_FLOW_OK) - old_ret = ret; - } - -done: - GST_LOG_OBJECT (parse, "chain leaving"); - return ret; -} - -/* Return the number of bytes available in the cached - * read buffer, if any */ -static guint -gst_base_parse_get_cached_available (GstBaseParse * parse) -{ - if (parse->priv->cache != NULL) { - gint64 cache_offset = GST_BUFFER_OFFSET (parse->priv->cache); - gint cache_size = gst_buffer_get_size (parse->priv->cache); - - if (parse->priv->offset >= cache_offset - && parse->priv->offset < cache_offset + cache_size) - return cache_size - (parse->priv->offset - cache_offset); /* Size of the cache minus consumed */ - } - return 0; -} - -/* pull @size bytes at current offset, - * i.e. at least try to and possibly return a shorter buffer if near the end */ -static GstFlowReturn -gst_base_parse_pull_range (GstBaseParse * parse, guint size, - GstBuffer ** buffer) -{ - GstFlowReturn ret = GST_FLOW_OK; - guint read_size; - - g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR); - - /* Caching here actually makes much less difference than one would expect. - * We do it mainly to avoid pulling buffers of 1 byte all the time */ - if (parse->priv->cache) { - gint64 cache_offset = GST_BUFFER_OFFSET (parse->priv->cache); - gint cache_size = gst_buffer_get_size (parse->priv->cache); - - if (cache_offset <= parse->priv->offset && - (parse->priv->offset + size) <= (cache_offset + cache_size)) { - *buffer = gst_buffer_copy_region (parse->priv->cache, GST_BUFFER_COPY_ALL, - parse->priv->offset - cache_offset, size); - GST_BUFFER_OFFSET (*buffer) = parse->priv->offset; - GST_LOG_OBJECT (parse, - "Satisfying read request of %u bytes from cached buffer with offset %" - G_GINT64_FORMAT, size, cache_offset); - return GST_FLOW_OK; - } - /* not enough data in the cache, free cache and get a new one */ - gst_buffer_unref (parse->priv->cache); - parse->priv->cache = NULL; - } - - /* refill the cache */ - read_size = MAX (64 * 1024, size); - GST_LOG_OBJECT (parse, - "Reading cache buffer of %u bytes from offset %" G_GINT64_FORMAT, - read_size, parse->priv->offset); - ret = - gst_pad_pull_range (parse->sinkpad, parse->priv->offset, read_size, - &parse->priv->cache); - if (ret != GST_FLOW_OK) { - parse->priv->cache = NULL; - return ret; - } - - if (gst_buffer_get_size (parse->priv->cache) < size) { - GST_DEBUG_OBJECT (parse, "Returning short buffer at offset %" - G_GUINT64_FORMAT ": wanted %u bytes, got %" G_GSIZE_FORMAT " bytes", - parse->priv->offset, size, gst_buffer_get_size (parse->priv->cache)); - - *buffer = parse->priv->cache; - parse->priv->cache = NULL; - - return GST_FLOW_OK; - } - - GST_BUFFER_OFFSET (parse->priv->cache) = parse->priv->offset; - - *buffer = - gst_buffer_copy_region (parse->priv->cache, GST_BUFFER_COPY_ALL, 0, size); - GST_BUFFER_OFFSET (*buffer) = parse->priv->offset; - - return GST_FLOW_OK; -} - -static GstFlowReturn -gst_base_parse_handle_previous_fragment (GstBaseParse * parse) -{ - gint64 offset = 0; - GstClockTime ts = 0; - GstBuffer *buffer; - GstFlowReturn ret; - - GST_DEBUG_OBJECT (parse, "fragment ended; last_ts = %" GST_TIME_FORMAT - ", last_offset = %" G_GINT64_FORMAT, - GST_TIME_ARGS (parse->priv->last_pts), parse->priv->last_offset); - - if (!parse->priv->last_offset - || parse->priv->last_pts <= parse->segment.start) { - GST_DEBUG_OBJECT (parse, "past start of segment %" GST_TIME_FORMAT, - GST_TIME_ARGS (parse->segment.start)); - ret = GST_FLOW_EOS; - goto exit; - } - - /* last fragment started at last_offset / last_ts; - * seek back 10s capped at 1MB */ - if (parse->priv->last_pts >= 10 * GST_SECOND) - ts = parse->priv->last_pts - 10 * GST_SECOND; - /* if we are exact now, we will be more so going backwards */ - if (parse->priv->exact_position) { - offset = gst_base_parse_find_offset (parse, ts, TRUE, NULL); - } else { - if (!gst_base_parse_convert (parse, GST_FORMAT_TIME, ts, - GST_FORMAT_BYTES, &offset)) { - GST_DEBUG_OBJECT (parse, "conversion failed, only BYTE based"); - } - } - offset = CLAMP (offset, parse->priv->last_offset - 1024 * 1024, - parse->priv->last_offset - 1024); - offset = MAX (0, offset); - - GST_DEBUG_OBJECT (parse, "next fragment from offset %" G_GINT64_FORMAT, - offset); - parse->priv->offset = offset; - - ret = gst_base_parse_pull_range (parse, parse->priv->last_offset - offset, - &buffer); - if (ret != GST_FLOW_OK) - goto exit; - - /* offset will increase again as fragment is processed/parsed */ - parse->priv->last_offset = offset; - - gst_base_parse_start_fragment (parse); - gst_adapter_push (parse->priv->adapter, buffer); - ret = gst_base_parse_finish_fragment (parse, TRUE); - if (ret != GST_FLOW_OK) - goto exit; - - /* force previous fragment */ - parse->priv->offset = -1; - -exit: - return ret; -} - -/* PULL mode: - * pull and scan for next frame starting from current offset - * adjusts sync, drain and offset going along */ -static GstFlowReturn -gst_base_parse_scan_frame (GstBaseParse * parse, GstBaseParseClass * klass) -{ - GstBuffer *buffer; - GstFlowReturn ret = GST_FLOW_OK; - guint fsize, min_size; - gint flushed = 0; - gint skip = 0; - - GST_LOG_OBJECT (parse, "scanning for frame at offset %" G_GUINT64_FORMAT - " (%#" G_GINT64_MODIFIER "x)", parse->priv->offset, parse->priv->offset); - - /* let's make this efficient for all subclass once and for all; - * maybe it does not need this much, but in the latter case, we know we are - * in pull mode here and might as well try to read and supply more anyway, - * so start with the cached buffer, or if that's shrunk below 1024 bytes, - * pull a new cache buffer */ - fsize = gst_base_parse_get_cached_available (parse); - if (fsize < 1024) - fsize = 64 * 1024; - - while (TRUE) { - min_size = MAX (parse->priv->min_frame_size, fsize); - - GST_LOG_OBJECT (parse, "reading buffer size %u", min_size); - - parse->priv->drain = FALSE; - ret = gst_base_parse_pull_range (parse, min_size, &buffer); - if (ret != GST_FLOW_OK) - goto done; - - /* if we got a short read, inform subclass we are draining leftover - * and no more is to be expected */ - if (gst_buffer_get_size (buffer) < min_size) { - GST_LOG_OBJECT (parse, "... but did not get that; marked draining"); - parse->priv->drain = TRUE; - } - - if (parse->priv->detecting) { - ret = klass->detect (parse, buffer); - if (ret == GST_FLOW_NOT_NEGOTIATED) { - /* If draining we error out, otherwise request a buffer - * with 64kb more */ - if (parse->priv->drain) { - gst_buffer_unref (buffer); - GST_ERROR_OBJECT (parse, "Failed to detect format but draining"); - return GST_FLOW_ERROR; - } else { - /* Double our frame size, or increment by at most 64KB */ - fsize += MIN (fsize, 64 * 1024); - gst_buffer_unref (buffer); - continue; - } - } else if (ret != GST_FLOW_OK) { - gst_buffer_unref (buffer); - GST_ERROR_OBJECT (parse, "detect() returned %s", - gst_flow_get_name (ret)); - return ret; - } - - /* Else handle this buffer normally */ - } - - ret = gst_base_parse_handle_buffer (parse, buffer, &skip, &flushed); - if (ret != GST_FLOW_OK) - break; - - /* If a large amount of data was requested to be skipped, _handle_buffer - might have set the priv->skip flag to an extra amount on top of skip. - In pull mode, we can just pull from the new offset directly. */ - parse->priv->offset += parse->priv->skip; - parse->priv->skip = 0; - - /* something flushed means something happened, - * and we should bail out of this loop so as not to occupy - * the task thread indefinitely */ - if (flushed) { - GST_LOG_OBJECT (parse, "frame finished, breaking loop"); - break; - } - if (!skip) { - if (parse->priv->drain) { - /* nothing flushed, no skip and draining, so nothing left to do */ - GST_LOG_OBJECT (parse, "no activity or result when draining; " - "breaking loop and marking EOS"); - ret = GST_FLOW_EOS; - break; - } - /* otherwise, get some more data - * note that is checked this does not happen indefinitely */ - GST_LOG_OBJECT (parse, "getting some more data"); - - /* Double our frame size, or increment by at most 64KB */ - fsize += MIN (fsize, 64 * 1024); - } - } - -done: - return ret; -} - -/* Loop that is used in pull mode to retrieve data from upstream */ -static void -gst_base_parse_loop (GstPad * pad) -{ - GstBaseParse *parse; - GstBaseParseClass *klass; - GstFlowReturn ret = GST_FLOW_OK; - - parse = GST_BASE_PARSE (gst_pad_get_parent (pad)); - klass = GST_BASE_PARSE_GET_CLASS (parse); - - GST_LOG_OBJECT (parse, "Entering parse loop"); - - if (G_UNLIKELY (parse->priv->push_stream_start)) { - gchar *stream_id; - GstEvent *event; - - stream_id = - gst_pad_create_stream_id (parse->srcpad, GST_ELEMENT_CAST (parse), - NULL); - - event = gst_event_new_stream_start (stream_id); - gst_event_set_group_id (event, gst_util_group_id_next ()); - - GST_DEBUG_OBJECT (parse, "Pushing STREAM_START"); - gst_pad_push_event (parse->srcpad, event); - parse->priv->push_stream_start = FALSE; - g_free (stream_id); - } - - /* reverse playback: - * first fragment (closest to stop time) is handled normally below, - * then we pull in fragments going backwards */ - if (parse->segment.rate < 0.0) { - /* check if we jumped back to a previous fragment, - * which is a post-first fragment */ - if (parse->priv->offset < 0) { - ret = gst_base_parse_handle_previous_fragment (parse); - goto done; - } - } - - ret = gst_base_parse_scan_frame (parse, klass); - - /* eat expected eos signalling past segment in reverse playback */ - if (parse->segment.rate < 0.0 && ret == GST_FLOW_EOS && - parse->segment.position >= parse->segment.stop) { - GST_DEBUG_OBJECT (parse, "downstream has reached end of segment"); - /* push what was accumulated during loop run */ - gst_base_parse_finish_fragment (parse, FALSE); - /* force previous fragment */ - parse->priv->offset = -1; - goto eos; - } - - if (ret != GST_FLOW_OK) - goto done; - -done: - if (ret == GST_FLOW_EOS) - goto eos; - else if (ret != GST_FLOW_OK) - goto pause; - - gst_object_unref (parse); - return; - - /* ERRORS */ -eos: - { - ret = GST_FLOW_EOS; - GST_DEBUG_OBJECT (parse, "eos"); - /* fall-through */ - } -pause: - { - gboolean push_eos = FALSE; - - GST_DEBUG_OBJECT (parse, "pausing task, reason %s", - gst_flow_get_name (ret)); - gst_pad_pause_task (parse->sinkpad); - - if (ret == GST_FLOW_EOS) { - /* handle end-of-stream/segment */ - if (parse->segment.flags & GST_SEGMENT_FLAG_SEGMENT) { - gint64 stop; - - if ((stop = parse->segment.stop) == -1) - stop = parse->segment.duration; - - GST_DEBUG_OBJECT (parse, "sending segment_done"); - - gst_element_post_message - (GST_ELEMENT_CAST (parse), - gst_message_new_segment_done (GST_OBJECT_CAST (parse), - GST_FORMAT_TIME, stop)); - gst_pad_push_event (parse->srcpad, - gst_event_new_segment_done (GST_FORMAT_TIME, stop)); - } else { - /* If we STILL have zero frames processed, fire an error */ - if (parse->priv->framecount == 0) { - GST_ELEMENT_ERROR (parse, STREAM, WRONG_TYPE, - ("No valid frames found before end of stream"), (NULL)); - } - push_eos = TRUE; - } - } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) { - /* for fatal errors we post an error message, wrong-state is - * not fatal because it happens due to flushes and only means - * that we should stop now. */ - GST_ELEMENT_FLOW_ERROR (parse, ret); - push_eos = TRUE; - } - if (push_eos) { - GstEvent *topush; - if (parse->priv->estimated_duration <= 0) { - gst_base_parse_update_duration (parse); - } - /* Push pending events, including SEGMENT events */ - gst_base_parse_push_pending_events (parse); - - topush = gst_event_new_eos (); - GST_DEBUG_OBJECT (parse, "segment_seqnum:%" G_GUINT32_FORMAT, - parse->priv->segment_seqnum); - if (parse->priv->segment_seqnum != GST_SEQNUM_INVALID) - gst_event_set_seqnum (topush, parse->priv->segment_seqnum); - gst_pad_push_event (parse->srcpad, topush); - } - gst_object_unref (parse); - } -} - -static gboolean -gst_base_parse_sink_activate (GstPad * sinkpad, GstObject * parent) -{ - GstSchedulingFlags sched_flags; - GstBaseParse *parse; - GstQuery *query; - gboolean pull_mode; - - parse = GST_BASE_PARSE (parent); - - GST_DEBUG_OBJECT (parse, "sink activate"); - - query = gst_query_new_scheduling (); - if (!gst_pad_peer_query (sinkpad, query)) { - gst_query_unref (query); - goto baseparse_push; - } - - gst_query_parse_scheduling (query, &sched_flags, NULL, NULL, NULL); - - pull_mode = gst_query_has_scheduling_mode (query, GST_PAD_MODE_PULL) - && ((sched_flags & GST_SCHEDULING_FLAG_SEEKABLE) != 0); - - gst_query_unref (query); - - if (!pull_mode) - goto baseparse_push; - - GST_DEBUG_OBJECT (parse, "trying to activate in pull mode"); - if (!gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE)) - goto baseparse_push; - - parse->priv->push_stream_start = TRUE; - /* In pull mode, upstream is BYTES */ - parse->priv->upstream_format = GST_FORMAT_BYTES; - - return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_base_parse_loop, - sinkpad, NULL); - /* fallback */ -baseparse_push: - { - GST_DEBUG_OBJECT (parse, "trying to activate in push mode"); - return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE); - } -} - -static gboolean -gst_base_parse_activate (GstBaseParse * parse, gboolean active) -{ - GstBaseParseClass *klass; - gboolean result = TRUE; - - GST_DEBUG_OBJECT (parse, "activate %d", active); - - klass = GST_BASE_PARSE_GET_CLASS (parse); - - if (active) { - if (parse->priv->pad_mode == GST_PAD_MODE_NONE && klass->start) - result = klass->start (parse); - - /* If the subclass implements ::detect we want to - * call it for the first buffers now */ - parse->priv->detecting = (klass->detect != NULL); - } else { - /* We must make sure streaming has finished before resetting things - * and calling the ::stop vfunc */ - GST_PAD_STREAM_LOCK (parse->sinkpad); - GST_PAD_STREAM_UNLOCK (parse->sinkpad); - - if (parse->priv->pad_mode != GST_PAD_MODE_NONE && klass->stop) - result = klass->stop (parse); - - parse->priv->pad_mode = GST_PAD_MODE_NONE; - parse->priv->upstream_format = GST_FORMAT_UNDEFINED; - } - GST_DEBUG_OBJECT (parse, "activate return: %d", result); - return result; -} - -static gboolean -gst_base_parse_sink_activate_mode (GstPad * pad, GstObject * parent, - GstPadMode mode, gboolean active) -{ - gboolean result; - GstBaseParse *parse; - - parse = GST_BASE_PARSE (parent); - - GST_DEBUG_OBJECT (parse, "sink %sactivate in %s mode", - (active) ? "" : "de", gst_pad_mode_get_name (mode)); - - if (!gst_base_parse_activate (parse, active)) - goto activate_failed; - - switch (mode) { - case GST_PAD_MODE_PULL: - if (active) { - GstEvent *ev = gst_event_new_segment (&parse->segment); - - if (parse->priv->segment_seqnum != GST_SEQNUM_INVALID) - gst_event_set_seqnum (ev, parse->priv->segment_seqnum); - else - parse->priv->segment_seqnum = gst_event_get_seqnum (ev); - - parse->priv->pending_events = - g_list_prepend (parse->priv->pending_events, ev); - result = TRUE; - } else { - result = gst_pad_stop_task (pad); - } - break; - default: - result = TRUE; - break; - } - if (result) - parse->priv->pad_mode = active ? mode : GST_PAD_MODE_NONE; - - GST_DEBUG_OBJECT (parse, "sink activate return: %d", result); - - return result; - - /* ERRORS */ -activate_failed: - { - GST_DEBUG_OBJECT (parse, "activate failed"); - return FALSE; - } -} - -/** - * gst_base_parse_set_duration: - * @parse: #GstBaseParse. - * @fmt: #GstFormat. - * @duration: duration value. - * @interval: how often to update the duration estimate based on bitrate, or 0. - * - * Sets the duration of the currently playing media. Subclass can use this - * when it is able to determine duration and/or notices a change in the media - * duration. Alternatively, if @interval is non-zero (default), then stream - * duration is determined based on estimated bitrate, and updated every @interval - * frames. - */ -void -gst_base_parse_set_duration (GstBaseParse * parse, - GstFormat fmt, gint64 duration, gint interval) -{ - gint64 old_duration; - - g_return_if_fail (parse != NULL); - - if (parse->priv->upstream_has_duration) { - GST_DEBUG_OBJECT (parse, "using upstream duration; discarding update"); - goto exit; - } - - old_duration = parse->priv->duration; - - parse->priv->duration = duration; - parse->priv->duration_fmt = fmt; - GST_DEBUG_OBJECT (parse, "set duration: %" G_GINT64_FORMAT, duration); - if (fmt == GST_FORMAT_TIME && GST_CLOCK_TIME_IS_VALID (duration)) { - if (interval != 0) { - GST_DEBUG_OBJECT (parse, "valid duration provided, disabling estimate"); - interval = 0; - } - } - GST_DEBUG_OBJECT (parse, "set update interval: %d", interval); - parse->priv->update_interval = interval; - if (duration != old_duration) { - GstMessage *m; - - m = gst_message_new_duration_changed (GST_OBJECT (parse)); - gst_element_post_message (GST_ELEMENT (parse), m); - - /* TODO: what about duration tag? */ - } -exit: - return; -} - -/** - * gst_base_parse_set_average_bitrate: - * @parse: #GstBaseParse. - * @bitrate: average bitrate in bits/second - * - * Optionally sets the average bitrate detected in media (if non-zero), - * e.g. based on metadata, as it will be posted to the application. - * - * By default, announced average bitrate is estimated. The average bitrate - * is used to estimate the total duration of the stream and to estimate - * a seek position, if there's no index and the format is syncable - * (see gst_base_parse_set_syncable()). - */ -void -gst_base_parse_set_average_bitrate (GstBaseParse * parse, guint bitrate) -{ - parse->priv->bitrate = bitrate; - GST_DEBUG_OBJECT (parse, "bitrate %u", bitrate); -} - -/** - * gst_base_parse_set_min_frame_size: - * @parse: #GstBaseParse. - * @min_size: Minimum size in bytes of the data that this base class should - * give to subclass. - * - * Subclass can use this function to tell the base class that it needs to - * be given buffers of at least @min_size bytes. - */ -void -gst_base_parse_set_min_frame_size (GstBaseParse * parse, guint min_size) -{ - g_return_if_fail (parse != NULL); - - parse->priv->min_frame_size = min_size; - GST_LOG_OBJECT (parse, "set frame_min_size: %d", min_size); -} - -/** - * gst_base_parse_set_frame_rate: - * @parse: the #GstBaseParse to set - * @fps_num: frames per second (numerator). - * @fps_den: frames per second (denominator). - * @lead_in: frames needed before a segment for subsequent decode - * @lead_out: frames needed after a segment - * - * If frames per second is configured, parser can take care of buffer duration - * and timestamping. When performing segment clipping, or seeking to a specific - * location, a corresponding decoder might need an initial @lead_in and a - * following @lead_out number of frames to ensure the desired segment is - * entirely filled upon decoding. - */ -void -gst_base_parse_set_frame_rate (GstBaseParse * parse, guint fps_num, - guint fps_den, guint lead_in, guint lead_out) -{ - g_return_if_fail (parse != NULL); - - parse->priv->fps_num = fps_num; - parse->priv->fps_den = fps_den; - if (!fps_num || !fps_den) { - GST_DEBUG_OBJECT (parse, "invalid fps (%d/%d), ignoring parameters", - fps_num, fps_den); - fps_num = fps_den = 0; - parse->priv->frame_duration = GST_CLOCK_TIME_NONE; - parse->priv->lead_in = parse->priv->lead_out = 0; - parse->priv->lead_in_ts = parse->priv->lead_out_ts = 0; - } else { - parse->priv->frame_duration = - gst_util_uint64_scale (GST_SECOND, fps_den, fps_num); - parse->priv->lead_in = lead_in; - parse->priv->lead_out = lead_out; - parse->priv->lead_in_ts = - gst_util_uint64_scale (GST_SECOND, fps_den * lead_in, fps_num); - parse->priv->lead_out_ts = - gst_util_uint64_scale (GST_SECOND, fps_den * lead_out, fps_num); - /* aim for about 1.5s to estimate duration */ - if (parse->priv->update_interval < 0) { - guint64 interval = gst_util_uint64_scale (fps_num, 3, - G_GUINT64_CONSTANT (2) * fps_den); - - parse->priv->update_interval = MIN (interval, G_MAXINT); - - GST_LOG_OBJECT (parse, "estimated update interval to %d frames", - parse->priv->update_interval); - } - } - GST_LOG_OBJECT (parse, "set fps: %d/%d => duration: %" G_GINT64_FORMAT " ms", - fps_num, fps_den, parse->priv->frame_duration / GST_MSECOND); - GST_LOG_OBJECT (parse, "set lead in: %d frames = %" G_GUINT64_FORMAT " ms, " - "lead out: %d frames = %" G_GUINT64_FORMAT " ms", - lead_in, parse->priv->lead_in_ts / GST_MSECOND, - lead_out, parse->priv->lead_out_ts / GST_MSECOND); -} - -/** - * gst_base_parse_set_has_timing_info: - * @parse: a #GstBaseParse - * @has_timing: whether frames carry timing information - * - * Set if frames carry timing information which the subclass can (generally) - * parse and provide. In particular, intrinsic (rather than estimated) time - * can be obtained following a seek. - */ -void -gst_base_parse_set_has_timing_info (GstBaseParse * parse, gboolean has_timing) -{ - parse->priv->has_timing_info = has_timing; - GST_INFO_OBJECT (parse, "has_timing: %s", (has_timing) ? "yes" : "no"); -} - -/** - * gst_base_parse_set_syncable: - * @parse: a #GstBaseParse - * @syncable: set if frame starts can be identified - * - * Set if frame starts can be identified. This is set by default and - * determines whether seeking based on bitrate averages - * is possible for a format/stream. - */ -void -gst_base_parse_set_syncable (GstBaseParse * parse, gboolean syncable) -{ - parse->priv->syncable = syncable; - GST_INFO_OBJECT (parse, "syncable: %s", (syncable) ? "yes" : "no"); -} - -/** - * gst_base_parse_set_passthrough: - * @parse: a #GstBaseParse - * @passthrough: %TRUE if parser should run in passthrough mode - * - * Set if the nature of the format or configuration does not allow (much) - * parsing, and the parser should operate in passthrough mode (which only - * applies when operating in push mode). That is, incoming buffers are - * pushed through unmodified, i.e. no #GstBaseParseClass::handle_frame - * will be invoked, but #GstBaseParseClass::pre_push_frame will still be - * invoked, so subclass can perform as much or as little is appropriate for - * passthrough semantics in #GstBaseParseClass::pre_push_frame. - */ -void -gst_base_parse_set_passthrough (GstBaseParse * parse, gboolean passthrough) -{ - parse->priv->passthrough = passthrough; - GST_INFO_OBJECT (parse, "passthrough: %s", (passthrough) ? "yes" : "no"); -} - -/** - * gst_base_parse_set_pts_interpolation: - * @parse: a #GstBaseParse - * @pts_interpolate: %TRUE if parser should interpolate PTS timestamps - * - * By default, the base class will guess PTS timestamps using a simple - * interpolation (previous timestamp + duration), which is incorrect for - * data streams with reordering, where PTS can go backward. Sub-classes - * implementing such formats should disable PTS interpolation. - */ -void -gst_base_parse_set_pts_interpolation (GstBaseParse * parse, - gboolean pts_interpolate) -{ - parse->priv->pts_interpolate = pts_interpolate; - GST_INFO_OBJECT (parse, "PTS interpolation: %s", - (pts_interpolate) ? "yes" : "no"); -} - -/** - * gst_base_parse_set_infer_ts: - * @parse: a #GstBaseParse - * @infer_ts: %TRUE if parser should infer DTS/PTS from each other - * - * By default, the base class might try to infer PTS from DTS and vice - * versa. While this is generally correct for audio data, it may not - * be otherwise. Sub-classes implementing such formats should disable - * timestamp inferring. - */ -void -gst_base_parse_set_infer_ts (GstBaseParse * parse, gboolean infer_ts) -{ - parse->priv->infer_ts = infer_ts; - GST_INFO_OBJECT (parse, "TS inferring: %s", (infer_ts) ? "yes" : "no"); -} - -/** - * gst_base_parse_set_latency: - * @parse: a #GstBaseParse - * @min_latency: minimum parse latency - * @max_latency: maximum parse latency - * - * Sets the minimum and maximum (which may likely be equal) latency introduced - * by the parsing process. If there is such a latency, which depends on the - * particular parsing of the format, it typically corresponds to 1 frame duration. - */ -void -gst_base_parse_set_latency (GstBaseParse * parse, GstClockTime min_latency, - GstClockTime max_latency) -{ - g_return_if_fail (GST_CLOCK_TIME_IS_VALID (min_latency)); - g_return_if_fail (min_latency <= max_latency); - - GST_OBJECT_LOCK (parse); - parse->priv->min_latency = min_latency; - parse->priv->max_latency = max_latency; - GST_OBJECT_UNLOCK (parse); - GST_INFO_OBJECT (parse, "min/max latency %" GST_TIME_FORMAT ", %" - GST_TIME_FORMAT, GST_TIME_ARGS (min_latency), - GST_TIME_ARGS (max_latency)); -} - -static gboolean -gst_base_parse_get_duration (GstBaseParse * parse, GstFormat format, - GstClockTime * duration) -{ - gboolean res = FALSE; - - g_return_val_if_fail (duration != NULL, FALSE); - - *duration = GST_CLOCK_TIME_NONE; - if (parse->priv->duration != -1 && format == parse->priv->duration_fmt) { - GST_LOG_OBJECT (parse, "using provided duration"); - *duration = parse->priv->duration; - res = TRUE; - } else if (parse->priv->duration != -1) { - GST_LOG_OBJECT (parse, "converting provided duration"); - res = gst_base_parse_convert (parse, parse->priv->duration_fmt, - parse->priv->duration, format, (gint64 *) duration); - } else if (format == GST_FORMAT_TIME && parse->priv->estimated_duration != -1) { - GST_LOG_OBJECT (parse, "using estimated duration"); - *duration = parse->priv->estimated_duration; - res = TRUE; - } else { - GST_LOG_OBJECT (parse, "cannot estimate duration"); - } - - GST_LOG_OBJECT (parse, "res: %d, duration %" GST_TIME_FORMAT, res, - GST_TIME_ARGS (*duration)); - return res; -} - -static gboolean -gst_base_parse_src_query_default (GstBaseParse * parse, GstQuery * query) -{ - gboolean res = FALSE; - GstPad *pad; - - pad = GST_BASE_PARSE_SRC_PAD (parse); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - { - gint64 dest_value; - GstFormat format; - - GST_DEBUG_OBJECT (parse, "position query"); - gst_query_parse_position (query, &format, NULL); - - /* try upstream first */ - res = gst_pad_query_default (pad, GST_OBJECT_CAST (parse), query); - if (!res) { - /* Fall back on interpreting segment */ - GST_OBJECT_LOCK (parse); - /* Only reply BYTES if upstream is in BYTES already, otherwise - * we're not in charge */ - if (format == GST_FORMAT_BYTES - && parse->priv->upstream_format == GST_FORMAT_BYTES) { - dest_value = parse->priv->offset; - res = TRUE; - } else if (format == parse->segment.format && - GST_CLOCK_TIME_IS_VALID (parse->segment.position)) { - dest_value = gst_segment_to_stream_time (&parse->segment, - parse->segment.format, parse->segment.position); - res = TRUE; - } - GST_OBJECT_UNLOCK (parse); - if (!res && parse->priv->upstream_format == GST_FORMAT_BYTES) { - /* no precise result, upstream no idea either, then best estimate */ - /* priv->offset is updated in both PUSH/PULL modes, *iff* we're - * in charge of things */ - res = gst_base_parse_convert (parse, - GST_FORMAT_BYTES, parse->priv->offset, format, &dest_value); - } - if (res) - gst_query_set_position (query, format, dest_value); - } - break; - } - case GST_QUERY_DURATION: - { - GstFormat format; - GstClockTime duration; - - GST_DEBUG_OBJECT (parse, "duration query"); - gst_query_parse_duration (query, &format, NULL); - - /* consult upstream */ - res = gst_pad_query_default (pad, GST_OBJECT_CAST (parse), query); - - /* otherwise best estimate from us */ - if (!res) { - res = gst_base_parse_get_duration (parse, format, &duration); - if (res) - gst_query_set_duration (query, format, duration); - } - break; - } - case GST_QUERY_SEEKING: - { - GstFormat fmt; - GstClockTime duration = GST_CLOCK_TIME_NONE; - gboolean seekable = FALSE; - - GST_DEBUG_OBJECT (parse, "seeking query"); - gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL); - - /* consult upstream */ - res = gst_pad_query_default (pad, GST_OBJECT_CAST (parse), query); - - /* we may be able to help if in TIME */ - if (fmt == GST_FORMAT_TIME && gst_base_parse_is_seekable (parse)) { - gst_query_parse_seeking (query, &fmt, &seekable, NULL, NULL); - /* already OK if upstream takes care */ - GST_LOG_OBJECT (parse, "upstream handled %d, seekable %d", - res, seekable); - if (!(res && seekable)) { - if (!gst_base_parse_get_duration (parse, GST_FORMAT_TIME, &duration) - || duration == -1) { - /* seekable if we still have a chance to get duration later on */ - seekable = parse->priv->upstream_seekable && - (parse->priv->update_interval > 0); - } else { - seekable = parse->priv->upstream_seekable; - GST_LOG_OBJECT (parse, "already determine upstream seekabled: %d", - seekable); - } - gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration); - res = TRUE; - } - } - break; - } - case GST_QUERY_FORMATS: - gst_query_set_formatsv (query, 3, fmtlist); - res = TRUE; - break; - case GST_QUERY_CONVERT: - { - GstFormat src_format, dest_format; - gint64 src_value, dest_value; - - gst_query_parse_convert (query, &src_format, &src_value, - &dest_format, &dest_value); - - res = gst_base_parse_convert (parse, src_format, src_value, - dest_format, &dest_value); - if (res) { - gst_query_set_convert (query, src_format, src_value, - dest_format, dest_value); - } - break; - } - case GST_QUERY_LATENCY: - { - if ((res = gst_pad_peer_query (parse->sinkpad, query))) { - gboolean live; - GstClockTime min_latency, max_latency; - - gst_query_parse_latency (query, &live, &min_latency, &max_latency); - GST_DEBUG_OBJECT (parse, "Peer latency: live %d, min %" - GST_TIME_FORMAT " max %" GST_TIME_FORMAT, live, - GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency)); - - GST_OBJECT_LOCK (parse); - /* add our latency */ - min_latency += parse->priv->min_latency; - if (max_latency == -1 || parse->priv->max_latency == -1) - max_latency = -1; - else - max_latency += parse->priv->max_latency; - GST_OBJECT_UNLOCK (parse); - - gst_query_set_latency (query, live, min_latency, max_latency); - } - break; - } - case GST_QUERY_SEGMENT: - { - GstFormat format; - gint64 start, stop; - - format = parse->segment.format; - - start = - gst_segment_to_stream_time (&parse->segment, format, - parse->segment.start); - if ((stop = parse->segment.stop) == -1) - stop = parse->segment.duration; - else - stop = gst_segment_to_stream_time (&parse->segment, format, stop); - - gst_query_set_segment (query, parse->segment.rate, format, start, stop); - res = TRUE; - break; - } - default: - res = gst_pad_query_default (pad, GST_OBJECT_CAST (parse), query); - break; - } - return res; -} - -/* scans for a cluster start from @pos, - * return GST_FLOW_OK and frame position/time in @pos/@time if found */ -static GstFlowReturn -gst_base_parse_find_frame (GstBaseParse * parse, gint64 * pos, - GstClockTime * time, GstClockTime * duration) -{ - GstBaseParseClass *klass; - gint64 orig_offset; - gboolean orig_drain, orig_discont; - GstFlowReturn ret = GST_FLOW_OK; - GstBuffer *buf = NULL; - GstBaseParseFrame *sframe = NULL; - - g_return_val_if_fail (pos != NULL, GST_FLOW_ERROR); - g_return_val_if_fail (time != NULL, GST_FLOW_ERROR); - g_return_val_if_fail (duration != NULL, GST_FLOW_ERROR); - - klass = GST_BASE_PARSE_GET_CLASS (parse); - - *time = GST_CLOCK_TIME_NONE; - *duration = GST_CLOCK_TIME_NONE; - - /* save state */ - orig_offset = parse->priv->offset; - orig_discont = parse->priv->discont; - orig_drain = parse->priv->drain; - - GST_DEBUG_OBJECT (parse, "scanning for frame starting at %" G_GINT64_FORMAT - " (%#" G_GINT64_MODIFIER "x)", *pos, *pos); - - /* jump elsewhere and locate next frame */ - parse->priv->offset = *pos; - /* mark as scanning so frames don't get processed all the way */ - parse->priv->scanning = TRUE; - ret = gst_base_parse_scan_frame (parse, klass); - parse->priv->scanning = FALSE; - /* retrieve frame found during scan */ - sframe = parse->priv->scanned_frame; - parse->priv->scanned_frame = NULL; - - if (ret != GST_FLOW_OK || !sframe) - goto done; - - /* get offset first, subclass parsing might dump other stuff in there */ - *pos = sframe->offset; - buf = sframe->buffer; - g_assert (buf); - - /* but it should provide proper time */ - *time = GST_BUFFER_TIMESTAMP (buf); - *duration = GST_BUFFER_DURATION (buf); - - GST_LOG_OBJECT (parse, - "frame with time %" GST_TIME_FORMAT " at offset %" G_GINT64_FORMAT, - GST_TIME_ARGS (*time), *pos); - -done: - if (sframe) - gst_base_parse_frame_free (sframe); - - /* restore state */ - parse->priv->offset = orig_offset; - parse->priv->discont = orig_discont; - parse->priv->drain = orig_drain; - - return ret; -} - -/* bisect and scan through file for frame starting before @time, - * returns OK and @time/@offset if found, NONE and/or error otherwise - * If @time == G_MAXINT64, scan for duration ( == last frame) */ -static GstFlowReturn -gst_base_parse_locate_time (GstBaseParse * parse, GstClockTime * _time, - gint64 * _offset) -{ - GstFlowReturn ret = GST_FLOW_OK; - gint64 lpos, hpos, newpos; - GstClockTime time, ltime, htime, newtime, dur; - gboolean cont = TRUE; - const GstClockTime tolerance = TARGET_DIFFERENCE; - const guint chunk = 4 * 1024; - - g_return_val_if_fail (_time != NULL, GST_FLOW_ERROR); - g_return_val_if_fail (_offset != NULL, GST_FLOW_ERROR); - - GST_DEBUG_OBJECT (parse, "Bisecting for time %" GST_TIME_FORMAT, - GST_TIME_ARGS (*_time)); - - /* TODO also make keyframe aware if useful some day */ - - time = *_time; - - /* basic cases */ - if (time == 0) { - *_offset = 0; - return GST_FLOW_OK; - } - - if (time == -1) { - *_offset = -1; - return GST_FLOW_OK; - } - - /* do not know at first */ - *_offset = -1; - *_time = GST_CLOCK_TIME_NONE; - - /* need initial positions; start and end */ - lpos = parse->priv->first_frame_offset; - ltime = parse->priv->first_frame_pts; - /* try other one if no luck */ - if (!GST_CLOCK_TIME_IS_VALID (ltime)) - ltime = parse->priv->first_frame_dts; - if (!gst_base_parse_get_duration (parse, GST_FORMAT_TIME, &htime)) { - GST_DEBUG_OBJECT (parse, "Unknown time duration, cannot bisect"); - return GST_FLOW_ERROR; - } - hpos = parse->priv->upstream_size; - - GST_DEBUG_OBJECT (parse, - "Bisection initial bounds: bytes %" G_GINT64_FORMAT " %" G_GINT64_FORMAT - ", times %" GST_TIME_FORMAT " %" GST_TIME_FORMAT, lpos, hpos, - GST_TIME_ARGS (ltime), GST_TIME_ARGS (htime)); - - /* check preconditions are satisfied; - * start and end are needed, except for special case where we scan for - * last frame to determine duration */ - if (parse->priv->pad_mode != GST_PAD_MODE_PULL || !hpos || - !GST_CLOCK_TIME_IS_VALID (ltime) || - (!GST_CLOCK_TIME_IS_VALID (htime) && time != G_MAXINT64)) { - return GST_FLOW_OK; - } - - /* shortcut cases */ - if (time < ltime) { - goto exit; - } else if (time < ltime + tolerance) { - *_offset = lpos; - *_time = ltime; - goto exit; - } else if (time >= htime) { - *_offset = hpos; - *_time = htime; - goto exit; - } - - while (htime > ltime && cont) { - GST_LOG_OBJECT (parse, - "lpos: %" G_GUINT64_FORMAT ", ltime: %" GST_TIME_FORMAT, lpos, - GST_TIME_ARGS (ltime)); - GST_LOG_OBJECT (parse, - "hpos: %" G_GUINT64_FORMAT ", htime: %" GST_TIME_FORMAT, hpos, - GST_TIME_ARGS (htime)); - if (G_UNLIKELY (time == G_MAXINT64)) { - newpos = hpos; - } else if (G_LIKELY (hpos > lpos)) { - newpos = - gst_util_uint64_scale (hpos - lpos, time - ltime, htime - ltime) + - lpos - chunk; - } else { - /* should mean lpos == hpos, since lpos <= hpos is invariant */ - newpos = lpos; - /* we check this case once, but not forever, so break loop */ - cont = FALSE; - } - - /* ensure */ - newpos = CLAMP (newpos, lpos, hpos); - GST_LOG_OBJECT (parse, - "estimated _offset for %" GST_TIME_FORMAT ": %" G_GINT64_FORMAT, - GST_TIME_ARGS (time), newpos); - - ret = gst_base_parse_find_frame (parse, &newpos, &newtime, &dur); - if (ret == GST_FLOW_EOS) { - /* heuristic HACK */ - hpos = MAX (lpos, hpos - chunk); - continue; - } else if (ret != GST_FLOW_OK) { - goto exit; - } - - if (newtime == -1 || newpos == -1) { - GST_DEBUG_OBJECT (parse, "subclass did not provide metadata; aborting"); - break; - } - - if (G_UNLIKELY (time == G_MAXINT64)) { - *_offset = newpos; - *_time = newtime; - if (GST_CLOCK_TIME_IS_VALID (dur)) - *_time += dur; - break; - } else if (newtime > time) { - /* overshoot */ - hpos = (newpos >= hpos) ? MAX (lpos, hpos - chunk) : MAX (lpos, newpos); - htime = newtime; - } else if (newtime + tolerance > time) { - /* close enough undershoot */ - *_offset = newpos; - *_time = newtime; - break; - } else if (newtime < ltime) { - /* so a position beyond lpos resulted in earlier time than ltime ... */ - GST_DEBUG_OBJECT (parse, "non-ascending time; aborting"); - break; - } else { - /* undershoot too far */ - newpos += newpos == lpos ? chunk : 0; - lpos = CLAMP (newpos, lpos, hpos); - ltime = newtime; - } - } - -exit: - GST_LOG_OBJECT (parse, "return offset %" G_GINT64_FORMAT ", time %" - GST_TIME_FORMAT, *_offset, GST_TIME_ARGS (*_time)); - return ret; -} - -static gint64 -gst_base_parse_find_offset (GstBaseParse * parse, GstClockTime time, - gboolean before, GstClockTime * _ts) -{ - gint64 bytes = 0, ts = 0; - GstIndexEntry *entry = NULL; - - if (time == GST_CLOCK_TIME_NONE) { - ts = time; - bytes = -1; - goto exit; - } - - GST_BASE_PARSE_INDEX_LOCK (parse); - if (parse->priv->index) { - /* Let's check if we have an index entry for that time */ - entry = gst_index_get_assoc_entry (parse->priv->index, - parse->priv->index_id, - before ? GST_INDEX_LOOKUP_BEFORE : GST_INDEX_LOOKUP_AFTER, - GST_INDEX_ASSOCIATION_FLAG_KEY_UNIT, GST_FORMAT_TIME, time); - } - - if (entry) { - gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, &bytes); - gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &ts); - - GST_DEBUG_OBJECT (parse, "found index entry for %" GST_TIME_FORMAT - " at %" GST_TIME_FORMAT ", offset %" G_GINT64_FORMAT, - GST_TIME_ARGS (time), GST_TIME_ARGS (ts), bytes); - } else { - GST_DEBUG_OBJECT (parse, "no index entry found for %" GST_TIME_FORMAT, - GST_TIME_ARGS (time)); - if (!before) { - bytes = -1; - ts = GST_CLOCK_TIME_NONE; - } - } - GST_BASE_PARSE_INDEX_UNLOCK (parse); - -exit: - if (_ts) - *_ts = ts; - - return bytes; -} - -/* returns TRUE if seek succeeded */ -static gboolean -gst_base_parse_handle_seek (GstBaseParse * parse, GstEvent * event) -{ - gdouble rate; - GstFormat format; - GstSeekFlags flags; - GstSeekType start_type = GST_SEEK_TYPE_NONE, stop_type; - gboolean flush, update, res = TRUE, accurate; - gint64 start, stop, seekpos, seekstop; - GstSegment seeksegment = { 0, }; - GstClockTime start_ts; - guint32 seqnum; - GstEvent *segment_event; - - /* try upstream first, unless we're driving the streaming thread ourselves */ - if (parse->priv->pad_mode != GST_PAD_MODE_PULL) { - res = gst_pad_push_event (parse->sinkpad, gst_event_ref (event)); - if (res) - goto done; - } - - gst_event_parse_seek (event, &rate, &format, &flags, - &start_type, &start, &stop_type, &stop); - seqnum = gst_event_get_seqnum (event); - parse->priv->segment_seqnum = seqnum; - - GST_DEBUG_OBJECT (parse, "seek to format %s, rate %f, " - "start type %d at %" GST_TIME_FORMAT ", end type %d at %" - GST_TIME_FORMAT, gst_format_get_name (format), rate, - start_type, GST_TIME_ARGS (start), stop_type, GST_TIME_ARGS (stop)); - - /* we can only handle TIME, so check if subclass can convert - * to TIME format if it's some other format (such as DEFAULT) */ - if (format != GST_FORMAT_TIME) { - if (!gst_base_parse_convert (parse, format, start, GST_FORMAT_TIME, &start) - || !gst_base_parse_convert (parse, format, stop, GST_FORMAT_TIME, - &stop)) - goto no_convert_to_time; - - GST_INFO_OBJECT (parse, "converted %s format to start time " - "%" GST_TIME_FORMAT " and stop time %" GST_TIME_FORMAT, - gst_format_get_name (format), GST_TIME_ARGS (start), - GST_TIME_ARGS (stop)); - - format = GST_FORMAT_TIME; - } - - /* no negative rates in push mode (unless upstream takes care of that, but - * we've already tried upstream and it didn't handle the seek request) */ - if (rate < 0.0 && parse->priv->pad_mode == GST_PAD_MODE_PUSH) - goto negative_rate; - - if (start_type != GST_SEEK_TYPE_SET || - (stop_type != GST_SEEK_TYPE_SET && stop_type != GST_SEEK_TYPE_NONE)) - goto wrong_type; - - /* get flush flag */ - flush = flags & GST_SEEK_FLAG_FLUSH; - - /* copy segment, we need this because we still need the old - * segment when we close the current segment. */ - gst_segment_copy_into (&parse->segment, &seeksegment); - - GST_DEBUG_OBJECT (parse, "configuring seek"); - gst_segment_do_seek (&seeksegment, rate, format, flags, - start_type, start, stop_type, stop, &update); - - /* accurate seeking implies seek tables are used to obtain position, - * and the requested segment is maintained exactly, not adjusted any way */ - accurate = flags & GST_SEEK_FLAG_ACCURATE; - - /* maybe we can be accurate for (almost) free */ - gst_base_parse_find_offset (parse, seeksegment.position, TRUE, &start_ts); - if (seeksegment.position <= start_ts + TARGET_DIFFERENCE) { - GST_DEBUG_OBJECT (parse, "accurate seek possible"); - accurate = TRUE; - } - - if (accurate) { - GstClockTime startpos; - if (rate >= 0) - startpos = seeksegment.position; - else - startpos = start; - - /* accurate requested, so ... seek a bit before target */ - if (startpos < parse->priv->lead_in_ts) - startpos = 0; - else - startpos -= parse->priv->lead_in_ts; - - if (seeksegment.stop == -1 && seeksegment.duration != -1) - seeksegment.stop = seeksegment.start + seeksegment.duration; - - seekpos = gst_base_parse_find_offset (parse, startpos, TRUE, &start_ts); - seekstop = gst_base_parse_find_offset (parse, seeksegment.stop, FALSE, - NULL); - } else { - if (rate >= 0) - start_ts = seeksegment.position; - else - start_ts = start; - - if (seeksegment.stop == -1 && seeksegment.duration != -1) - seeksegment.stop = seeksegment.start + seeksegment.duration; - - if (!gst_base_parse_convert (parse, format, start_ts, - GST_FORMAT_BYTES, &seekpos)) - goto convert_failed; - if (!gst_base_parse_convert (parse, format, seeksegment.stop, - GST_FORMAT_BYTES, &seekstop)) - goto convert_failed; - } - - GST_DEBUG_OBJECT (parse, - "seek position %" G_GINT64_FORMAT " in bytes: %" G_GINT64_FORMAT, - start_ts, seekpos); - GST_DEBUG_OBJECT (parse, - "seek stop %" G_GINT64_FORMAT " in bytes: %" G_GINT64_FORMAT, - seeksegment.stop, seekstop); - - if (parse->priv->pad_mode == GST_PAD_MODE_PULL) { - gint64 last_stop; - - GST_DEBUG_OBJECT (parse, "seek in PULL mode"); - - if (flush) { - if (parse->srcpad) { - GstEvent *fevent = gst_event_new_flush_start (); - GST_DEBUG_OBJECT (parse, "sending flush start"); - - gst_event_set_seqnum (fevent, seqnum); - - gst_pad_push_event (parse->srcpad, gst_event_ref (fevent)); - /* unlock upstream pull_range */ - gst_pad_push_event (parse->sinkpad, fevent); - } - } else { - gst_pad_pause_task (parse->sinkpad); - } - - /* we should now be able to grab the streaming thread because we stopped it - * with the above flush/pause code */ - GST_PAD_STREAM_LOCK (parse->sinkpad); - - /* save current position */ - last_stop = parse->segment.position; - GST_DEBUG_OBJECT (parse, "stopped streaming at %" G_GINT64_FORMAT, - last_stop); - - /* now commit to new position */ - - /* prepare for streaming again */ - if (flush) { - GstEvent *fevent = gst_event_new_flush_stop (TRUE); - GST_DEBUG_OBJECT (parse, "sending flush stop"); - gst_event_set_seqnum (fevent, seqnum); - gst_pad_push_event (parse->srcpad, gst_event_ref (fevent)); - gst_pad_push_event (parse->sinkpad, fevent); - gst_base_parse_clear_queues (parse); - } - - memcpy (&parse->segment, &seeksegment, sizeof (GstSegment)); - - /* store the newsegment event so it can be sent from the streaming thread. */ - /* This will be sent later in _loop() */ - segment_event = gst_event_new_segment (&parse->segment); - gst_event_set_seqnum (segment_event, seqnum); - parse->priv->pending_events = - g_list_prepend (parse->priv->pending_events, segment_event); - - GST_DEBUG_OBJECT (parse, "Created newseg format %d, " - "start = %" GST_TIME_FORMAT ", stop = %" GST_TIME_FORMAT - ", time = %" GST_TIME_FORMAT, format, - GST_TIME_ARGS (parse->segment.start), - GST_TIME_ARGS (parse->segment.stop), - GST_TIME_ARGS (parse->segment.time)); - - /* one last chance in pull mode to stay accurate; - * maybe scan and subclass can find where to go */ - if (!accurate) { - gint64 scanpos; - GstClockTime ts = seeksegment.position; - - gst_base_parse_locate_time (parse, &ts, &scanpos); - if (scanpos >= 0) { - accurate = TRUE; - seekpos = scanpos; - /* running collected index now consists of several intervals, - * so optimized check no longer possible */ - parse->priv->index_last_valid = FALSE; - parse->priv->index_last_offset = 0; - parse->priv->index_last_ts = 0; - } - } - - /* mark discont if we are going to stream from another position. */ - if (seekpos != parse->priv->offset) { - GST_DEBUG_OBJECT (parse, - "mark DISCONT, we did a seek to another position"); - parse->priv->offset = seekpos; - parse->priv->last_offset = seekpos; - parse->priv->seen_keyframe = FALSE; - parse->priv->discont = TRUE; - parse->priv->next_dts = start_ts; - parse->priv->next_pts = GST_CLOCK_TIME_NONE; - parse->priv->last_dts = GST_CLOCK_TIME_NONE; - parse->priv->last_pts = GST_CLOCK_TIME_NONE; - parse->priv->sync_offset = seekpos; - parse->priv->exact_position = accurate; - } - - /* Start streaming thread if paused */ - gst_pad_start_task (parse->sinkpad, - (GstTaskFunction) gst_base_parse_loop, parse->sinkpad, NULL); - - GST_PAD_STREAM_UNLOCK (parse->sinkpad); - - /* handled seek */ - res = TRUE; - } else { - GstEvent *new_event; - GstBaseParseSeek *seek; - GstSeekFlags flags = (flush ? GST_SEEK_FLAG_FLUSH : GST_SEEK_FLAG_NONE); - - /* The only thing we need to do in PUSH-mode is to send the - seek event (in bytes) to upstream. Segment / flush handling happens - in corresponding src event handlers */ - GST_DEBUG_OBJECT (parse, "seek in PUSH mode"); - if (seekstop >= 0 && seekstop <= seekpos) - seekstop = seekpos; - new_event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, - GST_SEEK_TYPE_SET, seekpos, stop_type, seekstop); - gst_event_set_seqnum (new_event, seqnum); - - /* store segment info so its precise details can be reconstructed when - * receiving newsegment; - * this matters for all details when accurate seeking, - * is most useful to preserve NONE stop time otherwise */ - seek = g_new0 (GstBaseParseSeek, 1); - seek->segment = seeksegment; - seek->accurate = accurate; - seek->offset = seekpos; - seek->start_ts = start_ts; - GST_OBJECT_LOCK (parse); - /* less optimal, but preserves order */ - parse->priv->pending_seeks = - g_slist_append (parse->priv->pending_seeks, seek); - GST_OBJECT_UNLOCK (parse); - - res = gst_pad_push_event (parse->sinkpad, new_event); - - if (!res) { - GST_OBJECT_LOCK (parse); - parse->priv->pending_seeks = - g_slist_remove (parse->priv->pending_seeks, seek); - GST_OBJECT_UNLOCK (parse); - g_free (seek); - } - } - -done: - gst_event_unref (event); - return res; - - /* ERRORS */ -negative_rate: - { - GST_DEBUG_OBJECT (parse, "negative playback rates delegated upstream."); - res = FALSE; - goto done; - } -wrong_type: - { - GST_DEBUG_OBJECT (parse, "unsupported seek type."); - res = FALSE; - goto done; - } -no_convert_to_time: - { - GST_DEBUG_OBJECT (parse, "seek in %s format was requested, but subclass " - "couldn't convert that into TIME format", gst_format_get_name (format)); - res = FALSE; - goto done; - } -convert_failed: - { - GST_DEBUG_OBJECT (parse, "conversion TIME to BYTES failed."); - res = FALSE; - goto done; - } -} - -static void -gst_base_parse_set_upstream_tags (GstBaseParse * parse, GstTagList * taglist) -{ - if (taglist == parse->priv->upstream_tags) - return; - - if (parse->priv->upstream_tags) { - gst_tag_list_unref (parse->priv->upstream_tags); - parse->priv->upstream_tags = NULL; - } - - GST_INFO_OBJECT (parse, "upstream tags: %" GST_PTR_FORMAT, taglist); - - if (taglist != NULL) - parse->priv->upstream_tags = gst_tag_list_ref (taglist); - - gst_base_parse_check_bitrate_tags (parse); -} - -#if 0 -static void -gst_base_parse_set_index (GstElement * element, GstIndex * index) -{ - GstBaseParse *parse = GST_BASE_PARSE (element); - - GST_BASE_PARSE_INDEX_LOCK (parse); - if (parse->priv->index) - gst_object_unref (parse->priv->index); - if (index) { - parse->priv->index = gst_object_ref (index); - gst_index_get_writer_id (index, GST_OBJECT_CAST (element), - &parse->priv->index_id); - parse->priv->own_index = FALSE; - } else { - parse->priv->index = NULL; - } - GST_BASE_PARSE_INDEX_UNLOCK (parse); -} - -static GstIndex * -gst_base_parse_get_index (GstElement * element) -{ - GstBaseParse *parse = GST_BASE_PARSE (element); - GstIndex *result = NULL; - - GST_BASE_PARSE_INDEX_LOCK (parse); - if (parse->priv->index) - result = gst_object_ref (parse->priv->index); - GST_BASE_PARSE_INDEX_UNLOCK (parse); - - return result; -} -#endif - -static GstStateChangeReturn -gst_base_parse_change_state (GstElement * element, GstStateChange transition) -{ - GstBaseParse *parse; - GstStateChangeReturn result; - - parse = GST_BASE_PARSE (element); - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - /* If this is our own index destroy it as the - * old entries might be wrong for the new stream */ - GST_BASE_PARSE_INDEX_LOCK (parse); - if (parse->priv->own_index) { - gst_object_unref (parse->priv->index); - parse->priv->index = NULL; - parse->priv->own_index = FALSE; - } - - /* If no index was created, generate one */ - if (G_UNLIKELY (!parse->priv->index)) { - GST_DEBUG_OBJECT (parse, "no index provided creating our own"); - - parse->priv->index = g_object_new (gst_mem_index_get_type (), NULL); - gst_index_get_writer_id (parse->priv->index, GST_OBJECT (parse), - &parse->priv->index_id); - parse->priv->own_index = TRUE; - } - GST_BASE_PARSE_INDEX_UNLOCK (parse); - break; - default: - break; - } - - result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_base_parse_reset (parse); - break; - default: - break; - } - - return result; -} - -/** - * gst_base_parse_set_ts_at_offset: - * @parse: a #GstBaseParse - * @offset: offset into current buffer - * - * This function should only be called from a @handle_frame implementation. - * - * #GstBaseParse creates initial timestamps for frames by using the last - * timestamp seen in the stream before the frame starts. In certain - * cases, the correct timestamps will occur in the stream after the - * start of the frame, but before the start of the actual picture data. - * This function can be used to set the timestamps based on the offset - * into the frame data that the picture starts. - * - * Since: 1.2 - */ -void -gst_base_parse_set_ts_at_offset (GstBaseParse * parse, gsize offset) -{ - GstClockTime pts, dts; - - g_return_if_fail (GST_IS_BASE_PARSE (parse)); - - pts = gst_adapter_prev_pts_at_offset (parse->priv->adapter, offset, NULL); - dts = gst_adapter_prev_dts_at_offset (parse->priv->adapter, offset, NULL); - - if (!GST_CLOCK_TIME_IS_VALID (pts) || !GST_CLOCK_TIME_IS_VALID (dts)) { - GST_DEBUG_OBJECT (parse, - "offset adapter timestamps dts=%" GST_TIME_FORMAT " pts=%" - GST_TIME_FORMAT, GST_TIME_ARGS (dts), GST_TIME_ARGS (pts)); - } - if (GST_CLOCK_TIME_IS_VALID (pts) && (parse->priv->prev_pts != pts)) - parse->priv->prev_pts = parse->priv->next_pts = pts; - - if (GST_CLOCK_TIME_IS_VALID (dts) && (parse->priv->prev_dts != dts)) { - parse->priv->prev_dts = parse->priv->next_dts = dts; - parse->priv->prev_dts_from_pts = FALSE; - } -} - -/** - * gst_base_parse_merge_tags: - * @parse: a #GstBaseParse - * @tags: (allow-none): a #GstTagList to merge, or NULL to unset - * previously-set tags - * @mode: the #GstTagMergeMode to use, usually #GST_TAG_MERGE_REPLACE - * - * Sets the parser subclass's tags and how they should be merged with any - * upstream stream tags. This will override any tags previously-set - * with gst_base_parse_merge_tags(). - * - * Note that this is provided for convenience, and the subclass is - * not required to use this and can still do tag handling on its own. - * - * Since: 1.6 - */ -void -gst_base_parse_merge_tags (GstBaseParse * parse, GstTagList * tags, - GstTagMergeMode mode) -{ - g_return_if_fail (GST_IS_BASE_PARSE (parse)); - g_return_if_fail (tags == NULL || GST_IS_TAG_LIST (tags)); - g_return_if_fail (tags == NULL || mode != GST_TAG_MERGE_UNDEFINED); - - GST_OBJECT_LOCK (parse); - - if (tags != parse->priv->parser_tags) { - if (parse->priv->parser_tags) { - gst_tag_list_unref (parse->priv->parser_tags); - parse->priv->parser_tags = NULL; - parse->priv->parser_tags_merge_mode = GST_TAG_MERGE_APPEND; - } - if (tags) { - parse->priv->parser_tags = gst_tag_list_ref (tags); - parse->priv->parser_tags_merge_mode = mode; - } - - GST_DEBUG_OBJECT (parse, "setting parser tags to %" GST_PTR_FORMAT - " (mode %d)", tags, parse->priv->parser_tags_merge_mode); - - gst_base_parse_check_bitrate_tags (parse); - parse->priv->tags_changed = TRUE; - } - - GST_OBJECT_UNLOCK (parse); -} diff --git a/libs/gst/base/gstbaseparse.h b/libs/gst/base/gstbaseparse.h deleted file mode 100644 index 2614e22f5e..0000000000 --- a/libs/gst/base/gstbaseparse.h +++ /dev/null @@ -1,370 +0,0 @@ -/* GStreamer - * Copyright (C) 2008 Nokia Corporation. All rights reserved. - * - * Contact: Stefan Kost <stefan.kost@nokia.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_BASE_PARSE_H__ -#define __GST_BASE_PARSE_H__ - -#include <gst/gst.h> -#include <gst/base/base-prelude.h> - -G_BEGIN_DECLS - -#define GST_TYPE_BASE_PARSE (gst_base_parse_get_type()) -#define GST_BASE_PARSE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASE_PARSE,GstBaseParse)) -#define GST_BASE_PARSE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASE_PARSE,GstBaseParseClass)) -#define GST_BASE_PARSE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_BASE_PARSE,GstBaseParseClass)) -#define GST_IS_BASE_PARSE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASE_PARSE)) -#define GST_IS_BASE_PARSE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASE_PARSE)) -#define GST_BASE_PARSE_CAST(obj) ((GstBaseParse *)(obj)) - -/** - * GST_BASE_PARSE_SRC_PAD: - * @obj: base parse instance - * - * Gives the pointer to the source #GstPad object of the element. - */ -#define GST_BASE_PARSE_SRC_PAD(obj) (GST_BASE_PARSE_CAST (obj)->srcpad) - -/** - * GST_BASE_PARSE_SINK_PAD: - * @obj: base parse instance - * - * Gives the pointer to the sink #GstPad object of the element. - */ -#define GST_BASE_PARSE_SINK_PAD(obj) (GST_BASE_PARSE_CAST (obj)->sinkpad) - -/** - * GST_BASE_PARSE_FLOW_DROPPED: - * - * A #GstFlowReturn that can be returned from - * #GstBaseParseClass::handle_frame to indicate that no output buffer was - * generated, or from #GstBaseParseClass::pre_push_frame to to forego - * pushing buffer. - */ -#define GST_BASE_PARSE_FLOW_DROPPED GST_FLOW_CUSTOM_SUCCESS - -/* not public API, use accessor macros below */ -#define GST_BASE_PARSE_FLAG_LOST_SYNC (1 << 0) -#define GST_BASE_PARSE_FLAG_DRAINING (1 << 1) - -/** - * GST_BASE_PARSE_LOST_SYNC: - * @parse: base parse instance - * - * Obtains current sync status. - */ -#define GST_BASE_PARSE_LOST_SYNC(parse) (!!(GST_BASE_PARSE_CAST(parse)->flags & GST_BASE_PARSE_FLAG_LOST_SYNC)) - -/** - * GST_BASE_PARSE_DRAINING: - * @parse: base parse instance - * - * Obtains current drain status (ie. whether EOS has been received and - * the parser is now processing the frames at the end of the stream) - */ -#define GST_BASE_PARSE_DRAINING(parse) (!!(GST_BASE_PARSE_CAST(parse)->flags & GST_BASE_PARSE_FLAG_DRAINING)) - -/** - * GstBaseParseFrameFlags: - * @GST_BASE_PARSE_FRAME_FLAG_NONE: no flag - * @GST_BASE_PARSE_FRAME_FLAG_NEW_FRAME: set by baseclass if current frame - * is passed for processing to the subclass for the first time - * (and not set on subsequent calls with same data). - * @GST_BASE_PARSE_FRAME_FLAG_NO_FRAME: set to indicate this buffer should not be - * counted as frame, e.g. if this frame is dependent on a previous one. - * As it is not counted as a frame, bitrate increases but frame to time - * conversions are maintained. - * @GST_BASE_PARSE_FRAME_FLAG_CLIP: @pre_push_frame can set this to indicate - * that regular segment clipping can still be performed (as opposed to - * any custom one having been done). - * @GST_BASE_PARSE_FRAME_FLAG_DROP: indicates to @finish_frame that the - * the frame should be dropped (and might be handled internally by subclass) - * @GST_BASE_PARSE_FRAME_FLAG_QUEUE: indicates to @finish_frame that the - * the frame should be queued for now and processed fully later - * when the first non-queued frame is finished - * - * Flags to be used in a #GstBaseParseFrame. - */ -typedef enum { - GST_BASE_PARSE_FRAME_FLAG_NONE = 0, - GST_BASE_PARSE_FRAME_FLAG_NEW_FRAME = (1 << 0), - GST_BASE_PARSE_FRAME_FLAG_NO_FRAME = (1 << 1), - GST_BASE_PARSE_FRAME_FLAG_CLIP = (1 << 2), - GST_BASE_PARSE_FRAME_FLAG_DROP = (1 << 3), - GST_BASE_PARSE_FRAME_FLAG_QUEUE = (1 << 4) -} GstBaseParseFrameFlags; - -/** - * GstBaseParseFrame: - * @buffer: input data to be parsed for frames. - * @out_buffer: output data. - * @offset: media specific offset of input frame - * Note that a converter may have a different one on the frame's buffer. - * @overhead: subclass can set this to indicates the metadata overhead - * for the given frame, which is then used to enable more accurate bitrate - * computations. If this is -1, it is assumed that this frame should be - * skipped in bitrate calculation. - * @flags: a combination of input and output #GstBaseParseFrameFlags that - * convey additional context to subclass or allow subclass to tune - * subsequent #GstBaseParse actions. - * - * Frame (context) data passed to each frame parsing virtual methods. In - * addition to providing the data to be checked for a valid frame or an already - * identified frame, it conveys additional metadata or control information - * from and to the subclass w.r.t. the particular frame in question (rather - * than global parameters). Some of these may apply to each parsing stage, others - * only to some a particular one. These parameters are effectively zeroed at start - * of each frame's processing, i.e. parsing virtual method invocation sequence. - */ -typedef struct { - GstBuffer * buffer; - GstBuffer * out_buffer; - guint flags; - guint64 offset; - gint overhead; - /*< private >*/ - gint size; - guint _gst_reserved_i[2]; - gpointer _gst_reserved_p[2]; - guint _private_flags; -} GstBaseParseFrame; - -typedef struct _GstBaseParse GstBaseParse; -typedef struct _GstBaseParseClass GstBaseParseClass; -typedef struct _GstBaseParsePrivate GstBaseParsePrivate; - -/** - * GstBaseParse: - * @element: the parent element. - * - * The opaque #GstBaseParse data structure. - */ -struct _GstBaseParse { - /*< public >*/ - GstElement element; - - /*< protected >*/ - /* source and sink pads */ - GstPad *sinkpad; - GstPad *srcpad; - - guint flags; - - /* MT-protected (with STREAM_LOCK) */ - GstSegment segment; - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING_LARGE]; - GstBaseParsePrivate *priv; -}; - -/** - * GstBaseParseClass: - * @parent_class: the parent class - * @start: Optional. - * Called when the element starts processing. - * Allows opening external resources. - * @stop: Optional. - * Called when the element stops processing. - * Allows closing external resources. - * @set_sink_caps: Optional. - * Allows the subclass to be notified of the actual caps set. - * @get_sink_caps: Optional. - * Allows the subclass to do its own sink get caps if needed. - * @handle_frame: Parses the input data into valid frames as defined by subclass - * which should be passed to gst_base_parse_finish_frame(). - * The frame's input buffer is guaranteed writable, - * whereas the input frame ownership is held by caller - * (so subclass should make a copy if it needs to hang on). - * Input buffer (data) is provided by baseclass with as much - * metadata set as possible by baseclass according to upstream - * information and/or subclass settings, - * though subclass may still set buffer timestamp and duration - * if desired. - * @convert: Optional. - * Convert between formats. - * @sink_event: Optional. - * Event handler on the sink pad. This function should chain - * up to the parent implementation to let the default handler - * run. - * @src_event: Optional. - * Event handler on the source pad. Should chain up to the - * parent to let the default handler run. - * @pre_push_frame: Optional. - * Called just prior to pushing a frame (after any pending - * events have been sent) to give subclass a chance to perform - * additional actions at this time (e.g. tag sending) or to - * decide whether this buffer should be dropped or not - * (e.g. custom segment clipping). - * @detect: Optional. - * Called until it doesn't return GST_FLOW_OK anymore for - * the first buffers. Can be used by the subclass to detect - * the stream format. - * @sink_query: Optional. - * Query handler on the sink pad. This function should chain - * up to the parent implementation to let the default handler - * run (Since: 1.2) - * @src_query: Optional. - * Query handler on the source pad. Should chain up to the - * parent to let the default handler run (Since: 1.2) - * - * Subclasses can override any of the available virtual methods or not, as - * needed. At minimum @handle_frame needs to be overridden. - */ -struct _GstBaseParseClass { - GstElementClass parent_class; - - /*< public >*/ - /* virtual methods for subclasses */ - - gboolean (*start) (GstBaseParse * parse); - - gboolean (*stop) (GstBaseParse * parse); - - gboolean (*set_sink_caps) (GstBaseParse * parse, - GstCaps * caps); - - GstFlowReturn (*handle_frame) (GstBaseParse * parse, - GstBaseParseFrame * frame, - gint * skipsize); - - GstFlowReturn (*pre_push_frame) (GstBaseParse * parse, - GstBaseParseFrame * frame); - - gboolean (*convert) (GstBaseParse * parse, - GstFormat src_format, - gint64 src_value, - GstFormat dest_format, - gint64 * dest_value); - - gboolean (*sink_event) (GstBaseParse * parse, - GstEvent * event); - - gboolean (*src_event) (GstBaseParse * parse, - GstEvent * event); - - GstCaps * (*get_sink_caps) (GstBaseParse * parse, - GstCaps * filter); - - GstFlowReturn (*detect) (GstBaseParse * parse, - GstBuffer * buffer); - - gboolean (*sink_query) (GstBaseParse * parse, - GstQuery * query); - - gboolean (*src_query) (GstBaseParse * parse, - GstQuery * query); - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING_LARGE - 2]; -}; - -GST_BASE_API -GType gst_base_parse_get_type (void); - -GST_BASE_API -GType gst_base_parse_frame_get_type (void); - -GST_BASE_API -GstBaseParseFrame * gst_base_parse_frame_new (GstBuffer * buffer, - GstBaseParseFrameFlags flags, - gint overhead); -GST_BASE_API -void gst_base_parse_frame_init (GstBaseParseFrame * frame); - -GST_BASE_API -GstBaseParseFrame * gst_base_parse_frame_copy (GstBaseParseFrame * frame); -GST_BASE_API -void gst_base_parse_frame_free (GstBaseParseFrame * frame); - -GST_BASE_API -GstFlowReturn gst_base_parse_push_frame (GstBaseParse * parse, - GstBaseParseFrame * frame); -GST_BASE_API -GstFlowReturn gst_base_parse_finish_frame (GstBaseParse * parse, - GstBaseParseFrame * frame, - gint size); -GST_BASE_API -void gst_base_parse_set_duration (GstBaseParse * parse, - GstFormat fmt, - gint64 duration, - gint interval); -GST_BASE_API -void gst_base_parse_set_average_bitrate (GstBaseParse * parse, - guint bitrate); -GST_BASE_API -void gst_base_parse_set_min_frame_size (GstBaseParse * parse, - guint min_size); -GST_BASE_API -void gst_base_parse_set_has_timing_info (GstBaseParse * parse, - gboolean has_timing); -GST_BASE_API -void gst_base_parse_drain (GstBaseParse * parse); - -GST_BASE_API -void gst_base_parse_set_syncable (GstBaseParse * parse, - gboolean syncable); -GST_BASE_API -void gst_base_parse_set_passthrough (GstBaseParse * parse, - gboolean passthrough); -GST_BASE_API -void gst_base_parse_set_pts_interpolation (GstBaseParse * parse, - gboolean pts_interpolate); -GST_BASE_API -void gst_base_parse_set_infer_ts (GstBaseParse * parse, - gboolean infer_ts); -GST_BASE_API -void gst_base_parse_set_frame_rate (GstBaseParse * parse, - guint fps_num, - guint fps_den, - guint lead_in, - guint lead_out); -GST_BASE_API -void gst_base_parse_set_latency (GstBaseParse * parse, - GstClockTime min_latency, - GstClockTime max_latency); -GST_BASE_API -gboolean gst_base_parse_convert_default (GstBaseParse * parse, - GstFormat src_format, - gint64 src_value, - GstFormat dest_format, - gint64 * dest_value); -GST_BASE_API -gboolean gst_base_parse_add_index_entry (GstBaseParse * parse, - guint64 offset, - GstClockTime ts, - gboolean key, - gboolean force); -GST_BASE_API -void gst_base_parse_set_ts_at_offset (GstBaseParse *parse, - gsize offset); -GST_BASE_API -void gst_base_parse_merge_tags (GstBaseParse * parse, - GstTagList * tags, - GstTagMergeMode mode); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstBaseParseFrame, gst_base_parse_frame_free) - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstBaseParse, gst_object_unref) - -G_END_DECLS - -#endif /* __GST_BASE_PARSE_H__ */ diff --git a/libs/gst/base/gstbasesink.c b/libs/gst/base/gstbasesink.c deleted file mode 100644 index 0a116d6b9b..0000000000 --- a/libs/gst/base/gstbasesink.c +++ /dev/null @@ -1,5916 +0,0 @@ -/* GStreamer - * Copyright (C) 2005-2007 Wim Taymans <wim.taymans@gmail.com> - * - * gstbasesink.c: Base class for sink elements - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * SECTION:gstbasesink - * @title: GstBaseSink - * @short_description: Base class for sink elements - * @see_also: #GstBaseTransform, #GstBaseSrc - * - * #GstBaseSink is the base class for sink elements in GStreamer, such as - * xvimagesink or filesink. It is a layer on top of #GstElement that provides a - * simplified interface to plugin writers. #GstBaseSink handles many details - * for you, for example: preroll, clock synchronization, state changes, - * activation in push or pull mode, and queries. - * - * In most cases, when writing sink elements, there is no need to implement - * class methods from #GstElement or to set functions on pads, because the - * #GstBaseSink infrastructure should be sufficient. - * - * #GstBaseSink provides support for exactly one sink pad, which should be - * named "sink". A sink implementation (subclass of #GstBaseSink) should - * install a pad template in its class_init function, like so: - * |[<!-- language="C" --> - * static void - * my_element_class_init (GstMyElementClass *klass) - * { - * GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); - * - * // sinktemplate should be a #GstStaticPadTemplate with direction - * // %GST_PAD_SINK and name "sink" - * gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate); - * - * gst_element_class_set_static_metadata (gstelement_class, - * "Sink name", - * "Sink", - * "My Sink element", - * "The author <my.sink@my.email>"); - * } - * ]| - * - * #GstBaseSink will handle the prerolling correctly. This means that it will - * return %GST_STATE_CHANGE_ASYNC from a state change to PAUSED until the first - * buffer arrives in this element. The base class will call the - * #GstBaseSinkClass::preroll vmethod with this preroll buffer and will then - * commit the state change to the next asynchronously pending state. - * - * When the element is set to PLAYING, #GstBaseSink will synchronise on the - * clock using the times returned from #GstBaseSinkClass::get_times. If this - * function returns %GST_CLOCK_TIME_NONE for the start time, no synchronisation - * will be done. Synchronisation can be disabled entirely by setting the object - * #GstBaseSink:sync property to %FALSE. - * - * After synchronisation the virtual method #GstBaseSinkClass::render will be - * called. Subclasses should minimally implement this method. - * - * Subclasses that synchronise on the clock in the #GstBaseSinkClass::render - * method are supported as well. These classes typically receive a buffer in - * the render method and can then potentially block on the clock while - * rendering. A typical example is an audiosink. - * These subclasses can use gst_base_sink_wait_preroll() to perform the - * blocking wait. - * - * Upon receiving the EOS event in the PLAYING state, #GstBaseSink will wait - * for the clock to reach the time indicated by the stop time of the last - * #GstBaseSinkClass::get_times call before posting an EOS message. When the - * element receives EOS in PAUSED, preroll completes, the event is queued and an - * EOS message is posted when going to PLAYING. - * - * #GstBaseSink will internally use the %GST_EVENT_SEGMENT events to schedule - * synchronisation and clipping of buffers. Buffers that fall completely outside - * of the current segment are dropped. Buffers that fall partially in the - * segment are rendered (and prerolled). Subclasses should do any subbuffer - * clipping themselves when needed. - * - * #GstBaseSink will by default report the current playback position in - * %GST_FORMAT_TIME based on the current clock time and segment information. - * If no clock has been set on the element, the query will be forwarded - * upstream. - * - * The #GstBaseSinkClass::set_caps function will be called when the subclass - * should configure itself to process a specific media type. - * - * The #GstBaseSinkClass::start and #GstBaseSinkClass::stop virtual methods - * will be called when resources should be allocated. Any - * #GstBaseSinkClass::preroll, #GstBaseSinkClass::render and - * #GstBaseSinkClass::set_caps function will be called between the - * #GstBaseSinkClass::start and #GstBaseSinkClass::stop calls. - * - * The #GstBaseSinkClass::event virtual method will be called when an event is - * received by #GstBaseSink. Normally this method should only be overridden by - * very specific elements (such as file sinks) which need to handle the - * newsegment event specially. - * - * The #GstBaseSinkClass::unlock method is called when the elements should - * unblock any blocking operations they perform in the - * #GstBaseSinkClass::render method. This is mostly useful when the - * #GstBaseSinkClass::render method performs a blocking write on a file - * descriptor, for example. - * - * The #GstBaseSink:max-lateness property affects how the sink deals with - * buffers that arrive too late in the sink. A buffer arrives too late in the - * sink when the presentation time (as a combination of the last segment, buffer - * timestamp and element base_time) plus the duration is before the current - * time of the clock. - * If the frame is later than max-lateness, the sink will drop the buffer - * without calling the render method. - * This feature is disabled if sync is disabled, the - * #GstBaseSinkClass::get_times method does not return a valid start time or - * max-lateness is set to -1 (the default). - * Subclasses can use gst_base_sink_set_max_lateness() to configure the - * max-lateness value. - * - * The #GstBaseSink:qos property will enable the quality-of-service features of - * the basesink which gather statistics about the real-time performance of the - * clock synchronisation. For each buffer received in the sink, statistics are - * gathered and a QOS event is sent upstream with these numbers. This - * information can then be used by upstream elements to reduce their processing - * rate, for example. - * - * The #GstBaseSink:async property can be used to instruct the sink to never - * perform an ASYNC state change. This feature is mostly usable when dealing - * with non-synchronized streams or sparse streams. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include <gst/gst_private.h> - -#include "gstbasesink.h" -#include <gst/gst-i18n-lib.h> - -GST_DEBUG_CATEGORY_STATIC (gst_base_sink_debug); -#define GST_CAT_DEFAULT gst_base_sink_debug - -#define GST_FLOW_STEP GST_FLOW_CUSTOM_ERROR - -typedef struct -{ - gboolean valid; /* if this info is valid */ - guint32 seqnum; /* the seqnum of the STEP event */ - GstFormat format; /* the format of the amount */ - guint64 amount; /* the total amount of data to skip */ - guint64 position; /* the position in the stepped data */ - guint64 duration; /* the duration in time of the skipped data */ - guint64 start; /* running_time of the start */ - gdouble rate; /* rate of skipping */ - gdouble start_rate; /* rate before skipping */ - guint64 start_start; /* start position skipping */ - guint64 start_stop; /* stop position skipping */ - gboolean flush; /* if this was a flushing step */ - gboolean intermediate; /* if this is an intermediate step */ - gboolean need_preroll; /* if we need preroll after this step */ -} GstStepInfo; - -struct _GstBaseSinkPrivate -{ - gint qos_enabled; /* ATOMIC */ - gboolean async_enabled; - GstClockTimeDiff ts_offset; - GstClockTime render_delay; - GstClockTime processing_deadline; - - /* start, stop of current buffer, stream time, used to report position */ - GstClockTime current_sstart; - GstClockTime current_sstop; - - /* start, stop and jitter of current buffer, running time */ - GstClockTime current_rstart; - GstClockTime current_rstop; - GstClockTimeDiff current_jitter; - /* the running time of the previous buffer */ - GstClockTime prev_rstart; - - /* EOS sync time in running time */ - GstClockTime eos_rtime; - - /* last buffer that arrived in time, running time */ - GstClockTime last_render_time; - /* when the last buffer left the sink, running time */ - GstClockTime last_left; - - /* running averages go here these are done on running time */ - GstClockTime avg_pt, avg_in_diff; - gdouble avg_rate; /* average with infinite window */ - - /* number of rendered and dropped frames */ - guint64 rendered; - guint64 dropped; - - /* latency stuff */ - GstClockTime latency; - - /* if we already committed the state */ - gboolean committed; - /* state change to playing ongoing */ - gboolean to_playing; - - /* when we received EOS */ - gboolean received_eos; - - /* when we are prerolled and able to report latency */ - gboolean have_latency; - - /* the last buffer we prerolled or rendered. Useful for making snapshots */ - gint enable_last_sample; /* atomic */ - GstBuffer *last_buffer; - GstCaps *last_caps; - GstBufferList *last_buffer_list; - - /* negotiated caps */ - GstCaps *caps; - - /* blocksize for pulling */ - guint blocksize; - - gboolean discont; - - /* seqnum of the stream */ - guint32 seqnum; - - gboolean call_preroll; - gboolean step_unlock; - - /* we have a pending and a current step operation */ - GstStepInfo current_step; - GstStepInfo pending_step; - - /* instant rate change state */ - /* seqnum of the last instant-rate-sync-time event - * received. %GST_SEQNUM_INVALID if there isn't one */ - guint32 instant_rate_sync_seqnum; - /* Active instant-rate multipler. 0.0 if nothing pending */ - gdouble instant_rate_multiplier; - /* seqnum of the last instant-rate event. - * %GST_SEQNUM_INVALID if there isn't one */ - guint32 last_instant_rate_seqnum; - guint32 segment_seqnum; - GstSegment upstream_segment; - /* Running time at the start of the last segment event - * or instant-rate switch in *our* segment, not upstream */ - GstClockTime last_anchor_running_time; - /* Difference between upstream running time and our own running time - * at the last segment event or instant-rate switch: - * upstream + offset = ours */ - GstClockTimeDiff instant_rate_offset; - - /* Cached GstClockID */ - GstClockID cached_clock_id; - - /* for throttling and QoS */ - GstClockTime earliest_in_time; - GstClockTime throttle_time; - - /* for rate control */ - guint64 max_bitrate; - GstClockTime rc_time; - GstClockTime rc_next; - gsize rc_accumulated; - - gboolean drop_out_of_segment; -}; - -#define DO_RUNNING_AVG(avg,val,size) (((val) + ((size)-1) * (avg)) / (size)) - -/* generic running average, this has a neutral window size */ -#define UPDATE_RUNNING_AVG(avg,val) DO_RUNNING_AVG(avg,val,8) - -/* the windows for these running averages are experimentally obtained. - * positive values get averaged more while negative values use a small - * window so we can react faster to badness. */ -#define UPDATE_RUNNING_AVG_P(avg,val) DO_RUNNING_AVG(avg,val,16) -#define UPDATE_RUNNING_AVG_N(avg,val) DO_RUNNING_AVG(avg,val,4) - -/* BaseSink properties */ - -#define DEFAULT_CAN_ACTIVATE_PULL FALSE /* fixme: enable me */ -#define DEFAULT_CAN_ACTIVATE_PUSH TRUE - -#define DEFAULT_SYNC TRUE -#define DEFAULT_MAX_LATENESS -1 -#define DEFAULT_QOS FALSE -#define DEFAULT_ASYNC TRUE -#define DEFAULT_TS_OFFSET 0 -#define DEFAULT_BLOCKSIZE 4096 -#define DEFAULT_RENDER_DELAY 0 -#define DEFAULT_ENABLE_LAST_SAMPLE TRUE -#define DEFAULT_THROTTLE_TIME 0 -#define DEFAULT_MAX_BITRATE 0 -#define DEFAULT_DROP_OUT_OF_SEGMENT TRUE -#define DEFAULT_PROCESSING_DEADLINE (20 * GST_MSECOND) - -enum -{ - PROP_0, - PROP_SYNC, - PROP_MAX_LATENESS, - PROP_QOS, - PROP_ASYNC, - PROP_TS_OFFSET, - PROP_ENABLE_LAST_SAMPLE, - PROP_LAST_SAMPLE, - PROP_BLOCKSIZE, - PROP_RENDER_DELAY, - PROP_THROTTLE_TIME, - PROP_MAX_BITRATE, - PROP_PROCESSING_DEADLINE, - PROP_STATS, - PROP_LAST -}; - -static GstElementClass *parent_class = NULL; -static gint private_offset = 0; - -static void gst_base_sink_class_init (GstBaseSinkClass * klass); -static void gst_base_sink_init (GstBaseSink * trans, gpointer g_class); -static void gst_base_sink_finalize (GObject * object); - -GType -gst_base_sink_get_type (void) -{ - static gsize base_sink_type = 0; - - if (g_once_init_enter (&base_sink_type)) { - GType _type; - static const GTypeInfo base_sink_info = { - sizeof (GstBaseSinkClass), - NULL, - NULL, - (GClassInitFunc) gst_base_sink_class_init, - NULL, - NULL, - sizeof (GstBaseSink), - 0, - (GInstanceInitFunc) gst_base_sink_init, - }; - - _type = g_type_register_static (GST_TYPE_ELEMENT, - "GstBaseSink", &base_sink_info, G_TYPE_FLAG_ABSTRACT); - - private_offset = - g_type_add_instance_private (_type, sizeof (GstBaseSinkPrivate)); - - g_once_init_leave (&base_sink_type, _type); - } - return base_sink_type; -} - -static inline GstBaseSinkPrivate * -gst_base_sink_get_instance_private (GstBaseSink * self) -{ - return (G_STRUCT_MEMBER_P (self, private_offset)); -} - -static void gst_base_sink_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_base_sink_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static gboolean gst_base_sink_send_event (GstElement * element, - GstEvent * event); -static gboolean default_element_query (GstElement * element, GstQuery * query); - -static GstCaps *gst_base_sink_default_get_caps (GstBaseSink * sink, - GstCaps * caps); -static gboolean gst_base_sink_default_set_caps (GstBaseSink * sink, - GstCaps * caps); -static void gst_base_sink_default_get_times (GstBaseSink * basesink, - GstBuffer * buffer, GstClockTime * start, GstClockTime * end); -static gboolean gst_base_sink_set_flushing (GstBaseSink * basesink, - GstPad * pad, gboolean flushing); -static gboolean gst_base_sink_default_activate_pull (GstBaseSink * basesink, - gboolean active); -static gboolean gst_base_sink_default_do_seek (GstBaseSink * sink, - GstSegment * segment); -static gboolean gst_base_sink_default_prepare_seek_segment (GstBaseSink * sink, - GstEvent * event, GstSegment * segment); - -static GstStateChangeReturn gst_base_sink_change_state (GstElement * element, - GstStateChange transition); - -static gboolean gst_base_sink_sink_query (GstPad * pad, GstObject * parent, - GstQuery * query); -static GstFlowReturn gst_base_sink_chain (GstPad * pad, GstObject * parent, - GstBuffer * buffer); -static GstFlowReturn gst_base_sink_chain_list (GstPad * pad, GstObject * parent, - GstBufferList * list); - -static void gst_base_sink_loop (GstPad * pad); -static gboolean gst_base_sink_pad_activate (GstPad * pad, GstObject * parent); -static gboolean gst_base_sink_pad_activate_mode (GstPad * pad, - GstObject * parent, GstPadMode mode, gboolean active); -static gboolean gst_base_sink_default_event (GstBaseSink * basesink, - GstEvent * event); -static GstFlowReturn gst_base_sink_default_wait_event (GstBaseSink * basesink, - GstEvent * event); -static gboolean gst_base_sink_event (GstPad * pad, GstObject * parent, - GstEvent * event); - -static gboolean gst_base_sink_default_query (GstBaseSink * sink, - GstQuery * query); - -static gboolean gst_base_sink_negotiate_pull (GstBaseSink * basesink); -static GstCaps *gst_base_sink_default_fixate (GstBaseSink * bsink, - GstCaps * caps); -static GstCaps *gst_base_sink_fixate (GstBaseSink * bsink, GstCaps * caps); - -/* check if an object was too late */ -static gboolean gst_base_sink_is_too_late (GstBaseSink * basesink, - GstMiniObject * obj, GstClockTime rstart, GstClockTime rstop, - GstClockReturn status, GstClockTimeDiff jitter, gboolean render); - -static void -gst_base_sink_class_init (GstBaseSinkClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - gobject_class = G_OBJECT_CLASS (klass); - gstelement_class = GST_ELEMENT_CLASS (klass); - - if (private_offset != 0) - g_type_class_adjust_private_offset (klass, &private_offset); - - GST_DEBUG_CATEGORY_INIT (gst_base_sink_debug, "basesink", 0, - "basesink element"); - - parent_class = g_type_class_peek_parent (klass); - - gobject_class->finalize = gst_base_sink_finalize; - gobject_class->set_property = gst_base_sink_set_property; - gobject_class->get_property = gst_base_sink_get_property; - - g_object_class_install_property (gobject_class, PROP_SYNC, - g_param_spec_boolean ("sync", "Sync", "Sync on the clock", DEFAULT_SYNC, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_MAX_LATENESS, - g_param_spec_int64 ("max-lateness", "Max Lateness", - "Maximum number of nanoseconds that a buffer can be late before it " - "is dropped (-1 unlimited)", -1, G_MAXINT64, DEFAULT_MAX_LATENESS, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_QOS, - g_param_spec_boolean ("qos", "Qos", - "Generate Quality-of-Service events upstream", DEFAULT_QOS, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - /** - * GstBaseSink:async: - * - * If set to %TRUE, the basesink will perform asynchronous state changes. - * When set to %FALSE, the sink will not signal the parent when it prerolls. - * Use this option when dealing with sparse streams or when synchronisation is - * not required. - */ - g_object_class_install_property (gobject_class, PROP_ASYNC, - g_param_spec_boolean ("async", "Async", - "Go asynchronously to PAUSED", DEFAULT_ASYNC, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - /** - * GstBaseSink:ts-offset: - * - * Controls the final synchronisation, a negative value will render the buffer - * earlier while a positive value delays playback. This property can be - * used to fix synchronisation in bad files. - */ - g_object_class_install_property (gobject_class, PROP_TS_OFFSET, - g_param_spec_int64 ("ts-offset", "TS Offset", - "Timestamp offset in nanoseconds", G_MININT64, G_MAXINT64, - DEFAULT_TS_OFFSET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - /** - * GstBaseSink:enable-last-sample: - * - * Enable the last-sample property. If %FALSE, basesink doesn't keep a - * reference to the last buffer arrived and the last-sample property is always - * set to %NULL. This can be useful if you need buffers to be released as soon - * as possible, eg. if you're using a buffer pool. - */ - g_object_class_install_property (gobject_class, PROP_ENABLE_LAST_SAMPLE, - g_param_spec_boolean ("enable-last-sample", "Enable Last Buffer", - "Enable the last-sample property", DEFAULT_ENABLE_LAST_SAMPLE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - /** - * GstBaseSink:last-sample: - * - * The last buffer that arrived in the sink and was used for preroll or for - * rendering. This property can be used to generate thumbnails. This property - * can be %NULL when the sink has not yet received a buffer. - */ - g_object_class_install_property (gobject_class, PROP_LAST_SAMPLE, - g_param_spec_boxed ("last-sample", "Last Sample", - "The last sample received in the sink", GST_TYPE_SAMPLE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - /** - * GstBaseSink:blocksize: - * - * The amount of bytes to pull when operating in pull mode. - */ - /* FIXME 2.0: blocksize property should be int, otherwise min>max.. */ - g_object_class_install_property (gobject_class, PROP_BLOCKSIZE, - g_param_spec_uint ("blocksize", "Block size", - "Size in bytes to pull per buffer (0 = default)", 0, G_MAXUINT, - DEFAULT_BLOCKSIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - /** - * GstBaseSink:render-delay: - * - * The additional delay between synchronisation and actual rendering of the - * media. This property will add additional latency to the device in order to - * make other sinks compensate for the delay. - */ - g_object_class_install_property (gobject_class, PROP_RENDER_DELAY, - g_param_spec_uint64 ("render-delay", "Render Delay", - "Additional render delay of the sink in nanoseconds", 0, G_MAXUINT64, - DEFAULT_RENDER_DELAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - /** - * GstBaseSink:throttle-time: - * - * The time to insert between buffers. This property can be used to control - * the maximum amount of buffers per second to render. Setting this property - * to a value bigger than 0 will make the sink create THROTTLE QoS events. - */ - g_object_class_install_property (gobject_class, PROP_THROTTLE_TIME, - g_param_spec_uint64 ("throttle-time", "Throttle time", - "The time to keep between rendered buffers (0 = disabled)", 0, - G_MAXUINT64, DEFAULT_THROTTLE_TIME, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - /** - * GstBaseSink:max-bitrate: - * - * Control the maximum amount of bits that will be rendered per second. - * Setting this property to a value bigger than 0 will make the sink delay - * rendering of the buffers when it would exceed to max-bitrate. - * - * Since: 1.2 - */ - g_object_class_install_property (gobject_class, PROP_MAX_BITRATE, - g_param_spec_uint64 ("max-bitrate", "Max Bitrate", - "The maximum bits per second to render (0 = disabled)", 0, - G_MAXUINT64, DEFAULT_MAX_BITRATE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - /** - * GstBaseSink:processing-deadline: - * - * Maximum amount of time (in nanoseconds) that the pipeline can take - * for processing the buffer. This is added to the latency of live - * pipelines. - * - * Since: 1.16 - */ - g_object_class_install_property (gobject_class, PROP_PROCESSING_DEADLINE, - g_param_spec_uint64 ("processing-deadline", "Processing deadline", - "Maximum processing time for a buffer in nanoseconds", 0, - G_MAXUINT64, DEFAULT_PROCESSING_DEADLINE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - - /** - * GstBaseSink:stats: - * - * Various #GstBaseSink statistics. This property returns a #GstStructure - * with name `application/x-gst-base-sink-stats` with the following fields: - * - * - "average-rate" G_TYPE_DOUBLE average frame rate - * - "dropped" G_TYPE_UINT64 Number of dropped frames - * - "rendered" G_TYPE_UINT64 Number of rendered frames - * - * Since: 1.18 - */ - g_object_class_install_property (gobject_class, PROP_STATS, - g_param_spec_boxed ("stats", "Statistics", - "Sink Statistics", GST_TYPE_STRUCTURE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_base_sink_change_state); - gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_base_sink_send_event); - gstelement_class->query = GST_DEBUG_FUNCPTR (default_element_query); - - klass->get_caps = GST_DEBUG_FUNCPTR (gst_base_sink_default_get_caps); - klass->set_caps = GST_DEBUG_FUNCPTR (gst_base_sink_default_set_caps); - klass->fixate = GST_DEBUG_FUNCPTR (gst_base_sink_default_fixate); - klass->activate_pull = - GST_DEBUG_FUNCPTR (gst_base_sink_default_activate_pull); - klass->get_times = GST_DEBUG_FUNCPTR (gst_base_sink_default_get_times); - klass->query = GST_DEBUG_FUNCPTR (gst_base_sink_default_query); - klass->event = GST_DEBUG_FUNCPTR (gst_base_sink_default_event); - klass->wait_event = GST_DEBUG_FUNCPTR (gst_base_sink_default_wait_event); - - /* Registering debug symbols for function pointers */ - GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_fixate); - GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_activate); - GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_pad_activate_mode); - GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_event); - GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_chain); - GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_chain_list); - GST_DEBUG_REGISTER_FUNCPTR (gst_base_sink_sink_query); -} - -static GstCaps * -gst_base_sink_query_caps (GstBaseSink * bsink, GstPad * pad, GstCaps * filter) -{ - GstBaseSinkClass *bclass; - GstCaps *caps = NULL; - gboolean fixed; - - bclass = GST_BASE_SINK_GET_CLASS (bsink); - fixed = GST_PAD_IS_FIXED_CAPS (pad); - - if (fixed || bsink->pad_mode == GST_PAD_MODE_PULL) { - /* if we are operating in pull mode or fixed caps, we only accept the - * currently negotiated caps */ - caps = gst_pad_get_current_caps (pad); - } - if (caps == NULL) { - if (bclass->get_caps) - caps = bclass->get_caps (bsink, filter); - - if (caps == NULL) { - GstPadTemplate *pad_template; - - pad_template = - gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), - "sink"); - if (pad_template != NULL) { - caps = gst_pad_template_get_caps (pad_template); - - if (filter) { - GstCaps *intersection; - - intersection = - gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST); - gst_caps_unref (caps); - caps = intersection; - } - } - } - } - - return caps; -} - -static GstCaps * -gst_base_sink_default_fixate (GstBaseSink * bsink, GstCaps * caps) -{ - GST_DEBUG_OBJECT (bsink, "using default caps fixate function"); - return gst_caps_fixate (caps); -} - -static GstCaps * -gst_base_sink_fixate (GstBaseSink * bsink, GstCaps * caps) -{ - GstBaseSinkClass *bclass; - - bclass = GST_BASE_SINK_GET_CLASS (bsink); - - if (bclass->fixate) - caps = bclass->fixate (bsink, caps); - - return caps; -} - -static void -gst_base_sink_init (GstBaseSink * basesink, gpointer g_class) -{ - GstPadTemplate *pad_template; - GstBaseSinkPrivate *priv; - - basesink->priv = priv = gst_base_sink_get_instance_private (basesink); - - pad_template = - gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "sink"); - g_return_if_fail (pad_template != NULL); - - basesink->sinkpad = gst_pad_new_from_template (pad_template, "sink"); - - gst_pad_set_activate_function (basesink->sinkpad, gst_base_sink_pad_activate); - gst_pad_set_activatemode_function (basesink->sinkpad, - gst_base_sink_pad_activate_mode); - gst_pad_set_query_function (basesink->sinkpad, gst_base_sink_sink_query); - gst_pad_set_event_function (basesink->sinkpad, gst_base_sink_event); - gst_pad_set_chain_function (basesink->sinkpad, gst_base_sink_chain); - gst_pad_set_chain_list_function (basesink->sinkpad, gst_base_sink_chain_list); - gst_element_add_pad (GST_ELEMENT_CAST (basesink), basesink->sinkpad); - - basesink->pad_mode = GST_PAD_MODE_NONE; - g_mutex_init (&basesink->preroll_lock); - g_cond_init (&basesink->preroll_cond); - priv->have_latency = FALSE; - - basesink->can_activate_push = DEFAULT_CAN_ACTIVATE_PUSH; - basesink->can_activate_pull = DEFAULT_CAN_ACTIVATE_PULL; - - basesink->sync = DEFAULT_SYNC; - basesink->max_lateness = DEFAULT_MAX_LATENESS; - g_atomic_int_set (&priv->qos_enabled, DEFAULT_QOS); - priv->async_enabled = DEFAULT_ASYNC; - priv->ts_offset = DEFAULT_TS_OFFSET; - priv->render_delay = DEFAULT_RENDER_DELAY; - priv->processing_deadline = DEFAULT_PROCESSING_DEADLINE; - priv->blocksize = DEFAULT_BLOCKSIZE; - priv->cached_clock_id = NULL; - g_atomic_int_set (&priv->enable_last_sample, DEFAULT_ENABLE_LAST_SAMPLE); - priv->throttle_time = DEFAULT_THROTTLE_TIME; - priv->max_bitrate = DEFAULT_MAX_BITRATE; - - priv->drop_out_of_segment = DEFAULT_DROP_OUT_OF_SEGMENT; - - GST_OBJECT_FLAG_SET (basesink, GST_ELEMENT_FLAG_SINK); -} - -static void -gst_base_sink_finalize (GObject * object) -{ - GstBaseSink *basesink; - - basesink = GST_BASE_SINK (object); - - g_mutex_clear (&basesink->preroll_lock); - g_cond_clear (&basesink->preroll_cond); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -/** - * gst_base_sink_set_sync: - * @sink: the sink - * @sync: the new sync value. - * - * Configures @sink to synchronize on the clock or not. When - * @sync is %FALSE, incoming samples will be played as fast as - * possible. If @sync is %TRUE, the timestamps of the incoming - * buffers will be used to schedule the exact render time of its - * contents. - */ -void -gst_base_sink_set_sync (GstBaseSink * sink, gboolean sync) -{ - g_return_if_fail (GST_IS_BASE_SINK (sink)); - - GST_OBJECT_LOCK (sink); - sink->sync = sync; - GST_OBJECT_UNLOCK (sink); -} - -/** - * gst_base_sink_get_sync: - * @sink: the sink - * - * Checks if @sink is currently configured to synchronize against the - * clock. - * - * Returns: %TRUE if the sink is configured to synchronize against the clock. - */ -gboolean -gst_base_sink_get_sync (GstBaseSink * sink) -{ - gboolean res; - - g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE); - - GST_OBJECT_LOCK (sink); - res = sink->sync; - GST_OBJECT_UNLOCK (sink); - - return res; -} - -/** - * gst_base_sink_set_drop_out_of_segment: - * @sink: the sink - * @drop_out_of_segment: drop buffers outside the segment - * - * Configure @sink to drop buffers which are outside the current segment - * - * Since: 1.12 - */ -void -gst_base_sink_set_drop_out_of_segment (GstBaseSink * sink, - gboolean drop_out_of_segment) -{ - g_return_if_fail (GST_IS_BASE_SINK (sink)); - - GST_OBJECT_LOCK (sink); - sink->priv->drop_out_of_segment = drop_out_of_segment; - GST_OBJECT_UNLOCK (sink); - -} - -/** - * gst_base_sink_get_drop_out_of_segment: - * @sink: the sink - * - * Checks if @sink is currently configured to drop buffers which are outside - * the current segment - * - * Returns: %TRUE if the sink is configured to drop buffers outside the - * current segment. - * - * Since: 1.12 - */ -gboolean -gst_base_sink_get_drop_out_of_segment (GstBaseSink * sink) -{ - gboolean res; - - g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE); - - GST_OBJECT_LOCK (sink); - res = sink->priv->drop_out_of_segment; - GST_OBJECT_UNLOCK (sink); - - return res; -} - -/** - * gst_base_sink_set_max_lateness: - * @sink: the sink - * @max_lateness: the new max lateness value. - * - * Sets the new max lateness value to @max_lateness. This value is - * used to decide if a buffer should be dropped or not based on the - * buffer timestamp and the current clock time. A value of -1 means - * an unlimited time. - */ -void -gst_base_sink_set_max_lateness (GstBaseSink * sink, gint64 max_lateness) -{ - g_return_if_fail (GST_IS_BASE_SINK (sink)); - - GST_OBJECT_LOCK (sink); - sink->max_lateness = max_lateness; - GST_OBJECT_UNLOCK (sink); -} - -/** - * gst_base_sink_get_max_lateness: - * @sink: the sink - * - * Gets the max lateness value. See gst_base_sink_set_max_lateness() for - * more details. - * - * Returns: The maximum time in nanoseconds that a buffer can be late - * before it is dropped and not rendered. A value of -1 means an - * unlimited time. - */ -gint64 -gst_base_sink_get_max_lateness (GstBaseSink * sink) -{ - gint64 res; - - g_return_val_if_fail (GST_IS_BASE_SINK (sink), -1); - - GST_OBJECT_LOCK (sink); - res = sink->max_lateness; - GST_OBJECT_UNLOCK (sink); - - return res; -} - -/** - * gst_base_sink_set_qos_enabled: - * @sink: the sink - * @enabled: the new qos value. - * - * Configures @sink to send Quality-of-Service events upstream. - */ -void -gst_base_sink_set_qos_enabled (GstBaseSink * sink, gboolean enabled) -{ - g_return_if_fail (GST_IS_BASE_SINK (sink)); - - g_atomic_int_set (&sink->priv->qos_enabled, enabled); -} - -/** - * gst_base_sink_is_qos_enabled: - * @sink: the sink - * - * Checks if @sink is currently configured to send Quality-of-Service events - * upstream. - * - * Returns: %TRUE if the sink is configured to perform Quality-of-Service. - */ -gboolean -gst_base_sink_is_qos_enabled (GstBaseSink * sink) -{ - gboolean res; - - g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE); - - res = g_atomic_int_get (&sink->priv->qos_enabled); - - return res; -} - -/** - * gst_base_sink_set_async_enabled: - * @sink: the sink - * @enabled: the new async value. - * - * Configures @sink to perform all state changes asynchronously. When async is - * disabled, the sink will immediately go to PAUSED instead of waiting for a - * preroll buffer. This feature is useful if the sink does not synchronize - * against the clock or when it is dealing with sparse streams. - */ -void -gst_base_sink_set_async_enabled (GstBaseSink * sink, gboolean enabled) -{ - g_return_if_fail (GST_IS_BASE_SINK (sink)); - - GST_BASE_SINK_PREROLL_LOCK (sink); - g_atomic_int_set (&sink->priv->async_enabled, enabled); - GST_LOG_OBJECT (sink, "set async enabled to %d", enabled); - GST_BASE_SINK_PREROLL_UNLOCK (sink); -} - -/** - * gst_base_sink_is_async_enabled: - * @sink: the sink - * - * Checks if @sink is currently configured to perform asynchronous state - * changes to PAUSED. - * - * Returns: %TRUE if the sink is configured to perform asynchronous state - * changes. - */ -gboolean -gst_base_sink_is_async_enabled (GstBaseSink * sink) -{ - gboolean res; - - g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE); - - res = g_atomic_int_get (&sink->priv->async_enabled); - - return res; -} - -/** - * gst_base_sink_set_ts_offset: - * @sink: the sink - * @offset: the new offset - * - * Adjust the synchronisation of @sink with @offset. A negative value will - * render buffers earlier than their timestamp. A positive value will delay - * rendering. This function can be used to fix playback of badly timestamped - * buffers. - */ -void -gst_base_sink_set_ts_offset (GstBaseSink * sink, GstClockTimeDiff offset) -{ - g_return_if_fail (GST_IS_BASE_SINK (sink)); - - GST_OBJECT_LOCK (sink); - sink->priv->ts_offset = offset; - GST_LOG_OBJECT (sink, "set time offset to %" G_GINT64_FORMAT, offset); - GST_OBJECT_UNLOCK (sink); -} - -/** - * gst_base_sink_get_ts_offset: - * @sink: the sink - * - * Get the synchronisation offset of @sink. - * - * Returns: The synchronisation offset. - */ -GstClockTimeDiff -gst_base_sink_get_ts_offset (GstBaseSink * sink) -{ - GstClockTimeDiff res; - - g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0); - - GST_OBJECT_LOCK (sink); - res = sink->priv->ts_offset; - GST_OBJECT_UNLOCK (sink); - - return res; -} - -/** - * gst_base_sink_get_last_sample: - * @sink: the sink - * - * Get the last sample that arrived in the sink and was used for preroll or for - * rendering. This property can be used to generate thumbnails. - * - * The #GstCaps on the sample can be used to determine the type of the buffer. - * - * Free-function: gst_sample_unref - * - * Returns: (transfer full) (nullable): a #GstSample. gst_sample_unref() after - * usage. This function returns %NULL when no buffer has arrived in the - * sink yet or when the sink is not in PAUSED or PLAYING. - */ -GstSample * -gst_base_sink_get_last_sample (GstBaseSink * sink) -{ - GstSample *res = NULL; - - g_return_val_if_fail (GST_IS_BASE_SINK (sink), NULL); - - GST_OBJECT_LOCK (sink); - if (sink->priv->last_buffer_list) { - GstBuffer *first_buffer = NULL; - - /* Set the first buffer in the list to last sample's buffer */ - first_buffer = gst_buffer_list_get (sink->priv->last_buffer_list, 0); - res = - gst_sample_new (first_buffer, sink->priv->last_caps, &sink->segment, - NULL); - gst_sample_set_buffer_list (res, sink->priv->last_buffer_list); - } else if (sink->priv->last_buffer) { - res = gst_sample_new (sink->priv->last_buffer, - sink->priv->last_caps, &sink->segment, NULL); - } - GST_OBJECT_UNLOCK (sink); - - return res; -} - -/* with OBJECT_LOCK */ -static void -gst_base_sink_set_last_buffer_unlocked (GstBaseSink * sink, GstBuffer * buffer) -{ - GstBuffer *old; - - old = sink->priv->last_buffer; - if (G_LIKELY (old != buffer)) { - GST_DEBUG_OBJECT (sink, "setting last buffer to %p", buffer); - if (G_LIKELY (buffer)) - gst_buffer_ref (buffer); - sink->priv->last_buffer = buffer; - if (buffer) - /* copy over the caps */ - gst_caps_replace (&sink->priv->last_caps, sink->priv->caps); - else - gst_caps_replace (&sink->priv->last_caps, NULL); - } else { - old = NULL; - } - /* avoid unreffing with the lock because cleanup code might want to take the - * lock too */ - if (G_LIKELY (old)) { - GST_OBJECT_UNLOCK (sink); - gst_buffer_unref (old); - GST_OBJECT_LOCK (sink); - } -} - -/* with OBJECT_LOCK */ -static void -gst_base_sink_set_last_buffer_list_unlocked (GstBaseSink * sink, - GstBufferList * buffer_list) -{ - GstBufferList *old; - - old = sink->priv->last_buffer_list; - if (G_LIKELY (old != buffer_list)) { - GST_DEBUG_OBJECT (sink, "setting last buffer list to %p", buffer_list); - if (G_LIKELY (buffer_list)) - gst_mini_object_ref (GST_MINI_OBJECT_CAST (buffer_list)); - sink->priv->last_buffer_list = buffer_list; - } else { - old = NULL; - } - - /* avoid unreffing with the lock because cleanup code might want to take the - * lock too */ - if (G_LIKELY (old)) { - GST_OBJECT_UNLOCK (sink); - gst_mini_object_unref (GST_MINI_OBJECT_CAST (old)); - GST_OBJECT_LOCK (sink); - } -} - -static void -gst_base_sink_set_last_buffer (GstBaseSink * sink, GstBuffer * buffer) -{ - if (!g_atomic_int_get (&sink->priv->enable_last_sample)) - return; - - GST_OBJECT_LOCK (sink); - gst_base_sink_set_last_buffer_unlocked (sink, buffer); - GST_OBJECT_UNLOCK (sink); -} - -static void -gst_base_sink_set_last_buffer_list (GstBaseSink * sink, - GstBufferList * buffer_list) -{ - if (!g_atomic_int_get (&sink->priv->enable_last_sample)) - return; - - GST_OBJECT_LOCK (sink); - gst_base_sink_set_last_buffer_list_unlocked (sink, buffer_list); - GST_OBJECT_UNLOCK (sink); -} - -/** - * gst_base_sink_set_last_sample_enabled: - * @sink: the sink - * @enabled: the new enable-last-sample value. - * - * Configures @sink to store the last received sample in the last-sample - * property. - */ -void -gst_base_sink_set_last_sample_enabled (GstBaseSink * sink, gboolean enabled) -{ - g_return_if_fail (GST_IS_BASE_SINK (sink)); - - /* Only take lock if we change the value */ - if (g_atomic_int_compare_and_exchange (&sink->priv->enable_last_sample, - !enabled, enabled) && !enabled) { - GST_OBJECT_LOCK (sink); - gst_base_sink_set_last_buffer_unlocked (sink, NULL); - gst_base_sink_set_last_buffer_list_unlocked (sink, NULL); - GST_OBJECT_UNLOCK (sink); - } -} - -/** - * gst_base_sink_is_last_sample_enabled: - * @sink: the sink - * - * Checks if @sink is currently configured to store the last received sample in - * the last-sample property. - * - * Returns: %TRUE if the sink is configured to store the last received sample. - */ -gboolean -gst_base_sink_is_last_sample_enabled (GstBaseSink * sink) -{ - g_return_val_if_fail (GST_IS_BASE_SINK (sink), FALSE); - - return g_atomic_int_get (&sink->priv->enable_last_sample); -} - -/** - * gst_base_sink_get_latency: - * @sink: the sink - * - * Get the currently configured latency. - * - * Returns: The configured latency. - */ -GstClockTime -gst_base_sink_get_latency (GstBaseSink * sink) -{ - GstClockTime res; - - GST_OBJECT_LOCK (sink); - res = sink->priv->latency; - GST_OBJECT_UNLOCK (sink); - - return res; -} - -/** - * gst_base_sink_query_latency: - * @sink: the sink - * @live: (out) (allow-none): if the sink is live - * @upstream_live: (out) (allow-none): if an upstream element is live - * @min_latency: (out) (allow-none): the min latency of the upstream elements - * @max_latency: (out) (allow-none): the max latency of the upstream elements - * - * Query the sink for the latency parameters. The latency will be queried from - * the upstream elements. @live will be %TRUE if @sink is configured to - * synchronize against the clock. @upstream_live will be %TRUE if an upstream - * element is live. - * - * If both @live and @upstream_live are %TRUE, the sink will want to compensate - * for the latency introduced by the upstream elements by setting the - * @min_latency to a strictly positive value. - * - * This function is mostly used by subclasses. - * - * Returns: %TRUE if the query succeeded. - */ -gboolean -gst_base_sink_query_latency (GstBaseSink * sink, gboolean * live, - gboolean * upstream_live, GstClockTime * min_latency, - GstClockTime * max_latency) -{ - gboolean l, us_live, res, have_latency; - GstClockTime min, max, render_delay, processing_deadline; - GstQuery *query; - GstClockTime us_min, us_max; - - /* we are live when we sync to the clock */ - GST_OBJECT_LOCK (sink); - l = sink->sync; - have_latency = sink->priv->have_latency; - render_delay = sink->priv->render_delay; - processing_deadline = sink->priv->processing_deadline; - GST_OBJECT_UNLOCK (sink); - - /* assume no latency */ - min = 0; - max = -1; - us_live = FALSE; - us_min = 0; - us_max = 0; - - if (have_latency) { - GST_DEBUG_OBJECT (sink, "we are ready for LATENCY query"); - /* we are ready for a latency query this is when we preroll or when we are - * not async. */ - query = gst_query_new_latency (); - - /* ask the peer for the latency */ - if ((res = gst_pad_peer_query (sink->sinkpad, query))) { - /* get upstream min and max latency */ - gst_query_parse_latency (query, &us_live, &us_min, &us_max); - - if (us_live) { - /* upstream live, use its latency, subclasses should use these - * values to create the complete latency. */ - min = us_min; - max = us_max; - - if (l) { - if (max == -1 || min + processing_deadline <= max) - min += processing_deadline; - else { - GST_ELEMENT_WARNING (sink, CORE, CLOCK, - (_("Pipeline construction is invalid, please add queues.")), - ("Not enough buffering available for " - " the processing deadline of %" GST_TIME_FORMAT - ", add enough queues to buffer %" GST_TIME_FORMAT - " additional data. Shortening processing latency to %" - GST_TIME_FORMAT ".", - GST_TIME_ARGS (processing_deadline), - GST_TIME_ARGS (min + processing_deadline - max), - GST_TIME_ARGS (max - min))); - min = max; - } - } - } - if (l) { - /* we need to add the render delay if we are live */ - min += render_delay; - if (max != -1) - max += render_delay; - } - } - gst_query_unref (query); - } else { - GST_DEBUG_OBJECT (sink, "we are not yet ready for LATENCY query"); - res = FALSE; - } - - /* not live, we tried to do the query, if it failed we return TRUE anyway */ - if (!res) { - if (!l) { - res = TRUE; - GST_DEBUG_OBJECT (sink, "latency query failed but we are not live"); - } else { - GST_DEBUG_OBJECT (sink, "latency query failed and we are live"); - } - } - - if (res) { - GST_DEBUG_OBJECT (sink, "latency query: live: %d, have_latency %d," - " upstream_live %d, min(%" GST_TIME_FORMAT ")=upstream(%" - GST_TIME_FORMAT ")+processing_deadline(%" GST_TIME_FORMAT - ")+render_delay(%" GST_TIME_FORMAT "), max(%" GST_TIME_FORMAT - ")=upstream(%" GST_TIME_FORMAT ")+render_delay(%" GST_TIME_FORMAT ")", - l, have_latency, us_live, GST_TIME_ARGS (min), GST_TIME_ARGS (us_min), - GST_TIME_ARGS (processing_deadline), GST_TIME_ARGS (render_delay), - GST_TIME_ARGS (max), GST_TIME_ARGS (us_max), - GST_TIME_ARGS (render_delay)); - - if (live) - *live = l; - if (upstream_live) - *upstream_live = us_live; - if (min_latency) - *min_latency = min; - if (max_latency) - *max_latency = max; - } - return res; -} - -/** - * gst_base_sink_set_render_delay: - * @sink: a #GstBaseSink - * @delay: the new delay - * - * Set the render delay in @sink to @delay. The render delay is the time - * between actual rendering of a buffer and its synchronisation time. Some - * devices might delay media rendering which can be compensated for with this - * function. - * - * After calling this function, this sink will report additional latency and - * other sinks will adjust their latency to delay the rendering of their media. - * - * This function is usually called by subclasses. - */ -void -gst_base_sink_set_render_delay (GstBaseSink * sink, GstClockTime delay) -{ - GstClockTime old_render_delay; - - g_return_if_fail (GST_IS_BASE_SINK (sink)); - g_return_if_fail (GST_CLOCK_TIME_IS_VALID (delay)); - - GST_OBJECT_LOCK (sink); - old_render_delay = sink->priv->render_delay; - sink->priv->render_delay = delay; - GST_LOG_OBJECT (sink, "set render delay to %" GST_TIME_FORMAT, - GST_TIME_ARGS (delay)); - GST_OBJECT_UNLOCK (sink); - - if (delay != old_render_delay) { - GST_DEBUG_OBJECT (sink, "posting latency changed"); - gst_element_post_message (GST_ELEMENT_CAST (sink), - gst_message_new_latency (GST_OBJECT_CAST (sink))); - } -} - -/** - * gst_base_sink_get_render_delay: - * @sink: a #GstBaseSink - * - * Get the render delay of @sink. see gst_base_sink_set_render_delay() for more - * information about the render delay. - * - * Returns: the render delay of @sink. - */ -GstClockTime -gst_base_sink_get_render_delay (GstBaseSink * sink) -{ - GstClockTimeDiff res; - - g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0); - - GST_OBJECT_LOCK (sink); - res = sink->priv->render_delay; - GST_OBJECT_UNLOCK (sink); - - return res; -} - -/** - * gst_base_sink_set_blocksize: - * @sink: a #GstBaseSink - * @blocksize: the blocksize in bytes - * - * Set the number of bytes that the sink will pull when it is operating in pull - * mode. - */ -/* FIXME 2.0: blocksize property should be int, otherwise min>max.. */ -void -gst_base_sink_set_blocksize (GstBaseSink * sink, guint blocksize) -{ - g_return_if_fail (GST_IS_BASE_SINK (sink)); - - GST_OBJECT_LOCK (sink); - sink->priv->blocksize = blocksize; - GST_LOG_OBJECT (sink, "set blocksize to %u", blocksize); - GST_OBJECT_UNLOCK (sink); -} - -/** - * gst_base_sink_get_blocksize: - * @sink: a #GstBaseSink - * - * Get the number of bytes that the sink will pull when it is operating in pull - * mode. - * - * Returns: the number of bytes @sink will pull in pull mode. - */ -/* FIXME 2.0: blocksize property should be int, otherwise min>max.. */ -guint -gst_base_sink_get_blocksize (GstBaseSink * sink) -{ - guint res; - - g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0); - - GST_OBJECT_LOCK (sink); - res = sink->priv->blocksize; - GST_OBJECT_UNLOCK (sink); - - return res; -} - -/** - * gst_base_sink_set_throttle_time: - * @sink: a #GstBaseSink - * @throttle: the throttle time in nanoseconds - * - * Set the time that will be inserted between rendered buffers. This - * can be used to control the maximum buffers per second that the sink - * will render. - */ -void -gst_base_sink_set_throttle_time (GstBaseSink * sink, guint64 throttle) -{ - g_return_if_fail (GST_IS_BASE_SINK (sink)); - - GST_OBJECT_LOCK (sink); - sink->priv->throttle_time = throttle; - GST_LOG_OBJECT (sink, "set throttle_time to %" G_GUINT64_FORMAT, throttle); - GST_OBJECT_UNLOCK (sink); -} - -/** - * gst_base_sink_get_throttle_time: - * @sink: a #GstBaseSink - * - * Get the time that will be inserted between frames to control the - * maximum buffers per second. - * - * Returns: the number of nanoseconds @sink will put between frames. - */ -guint64 -gst_base_sink_get_throttle_time (GstBaseSink * sink) -{ - guint64 res; - - g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0); - - GST_OBJECT_LOCK (sink); - res = sink->priv->throttle_time; - GST_OBJECT_UNLOCK (sink); - - return res; -} - -/** - * gst_base_sink_set_max_bitrate: - * @sink: a #GstBaseSink - * @max_bitrate: the max_bitrate in bits per second - * - * Set the maximum amount of bits per second that the sink will render. - * - * Since: 1.2 - */ -void -gst_base_sink_set_max_bitrate (GstBaseSink * sink, guint64 max_bitrate) -{ - g_return_if_fail (GST_IS_BASE_SINK (sink)); - - GST_OBJECT_LOCK (sink); - sink->priv->max_bitrate = max_bitrate; - GST_LOG_OBJECT (sink, "set max_bitrate to %" G_GUINT64_FORMAT, max_bitrate); - GST_OBJECT_UNLOCK (sink); -} - -/** - * gst_base_sink_get_max_bitrate: - * @sink: a #GstBaseSink - * - * Get the maximum amount of bits per second that the sink will render. - * - * Returns: the maximum number of bits per second @sink will render. - * - * Since: 1.2 - */ -guint64 -gst_base_sink_get_max_bitrate (GstBaseSink * sink) -{ - guint64 res; - - g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0); - - GST_OBJECT_LOCK (sink); - res = sink->priv->max_bitrate; - GST_OBJECT_UNLOCK (sink); - - return res; -} - -/** - * gst_base_sink_set_processing_deadline: - * @sink: a #GstBaseSink - * @processing_deadline: the new processing deadline in nanoseconds. - * - * Maximum amount of time (in nanoseconds) that the pipeline can take - * for processing the buffer. This is added to the latency of live - * pipelines. - * - * This function is usually called by subclasses. - * - * Since: 1.16 - */ -void -gst_base_sink_set_processing_deadline (GstBaseSink * sink, - GstClockTime processing_deadline) -{ - GstClockTime old_processing_deadline; - - g_return_if_fail (GST_IS_BASE_SINK (sink)); - - GST_OBJECT_LOCK (sink); - old_processing_deadline = sink->priv->processing_deadline; - sink->priv->processing_deadline = processing_deadline; - GST_LOG_OBJECT (sink, "set render processing_deadline to %" GST_TIME_FORMAT, - GST_TIME_ARGS (processing_deadline)); - GST_OBJECT_UNLOCK (sink); - - if (processing_deadline != old_processing_deadline) { - GST_DEBUG_OBJECT (sink, "posting latency changed"); - gst_element_post_message (GST_ELEMENT_CAST (sink), - gst_message_new_latency (GST_OBJECT_CAST (sink))); - } -} - -/** - * gst_base_sink_get_processing_deadline: - * @sink: a #GstBaseSink - * - * Get the processing deadline of @sink. see - * gst_base_sink_set_processing_deadline() for more information about - * the processing deadline. - * - * Returns: the processing deadline - * - * Since: 1.16 - */ -GstClockTime -gst_base_sink_get_processing_deadline (GstBaseSink * sink) -{ - GstClockTimeDiff res; - - g_return_val_if_fail (GST_IS_BASE_SINK (sink), 0); - - GST_OBJECT_LOCK (sink); - res = sink->priv->processing_deadline; - GST_OBJECT_UNLOCK (sink); - - return res; -} - -static void -gst_base_sink_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstBaseSink *sink = GST_BASE_SINK (object); - - switch (prop_id) { - case PROP_SYNC: - gst_base_sink_set_sync (sink, g_value_get_boolean (value)); - break; - case PROP_MAX_LATENESS: - gst_base_sink_set_max_lateness (sink, g_value_get_int64 (value)); - break; - case PROP_QOS: - gst_base_sink_set_qos_enabled (sink, g_value_get_boolean (value)); - break; - case PROP_ASYNC: - gst_base_sink_set_async_enabled (sink, g_value_get_boolean (value)); - break; - case PROP_TS_OFFSET: - gst_base_sink_set_ts_offset (sink, g_value_get_int64 (value)); - break; - case PROP_BLOCKSIZE: - gst_base_sink_set_blocksize (sink, g_value_get_uint (value)); - break; - case PROP_RENDER_DELAY: - gst_base_sink_set_render_delay (sink, g_value_get_uint64 (value)); - break; - case PROP_ENABLE_LAST_SAMPLE: - gst_base_sink_set_last_sample_enabled (sink, g_value_get_boolean (value)); - break; - case PROP_THROTTLE_TIME: - gst_base_sink_set_throttle_time (sink, g_value_get_uint64 (value)); - break; - case PROP_MAX_BITRATE: - gst_base_sink_set_max_bitrate (sink, g_value_get_uint64 (value)); - break; - case PROP_PROCESSING_DEADLINE: - gst_base_sink_set_processing_deadline (sink, g_value_get_uint64 (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_base_sink_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstBaseSink *sink = GST_BASE_SINK (object); - - switch (prop_id) { - case PROP_SYNC: - g_value_set_boolean (value, gst_base_sink_get_sync (sink)); - break; - case PROP_MAX_LATENESS: - g_value_set_int64 (value, gst_base_sink_get_max_lateness (sink)); - break; - case PROP_QOS: - g_value_set_boolean (value, gst_base_sink_is_qos_enabled (sink)); - break; - case PROP_ASYNC: - g_value_set_boolean (value, gst_base_sink_is_async_enabled (sink)); - break; - case PROP_TS_OFFSET: - g_value_set_int64 (value, gst_base_sink_get_ts_offset (sink)); - break; - case PROP_LAST_SAMPLE: - gst_value_take_sample (value, gst_base_sink_get_last_sample (sink)); - break; - case PROP_ENABLE_LAST_SAMPLE: - g_value_set_boolean (value, gst_base_sink_is_last_sample_enabled (sink)); - break; - case PROP_BLOCKSIZE: - g_value_set_uint (value, gst_base_sink_get_blocksize (sink)); - break; - case PROP_RENDER_DELAY: - g_value_set_uint64 (value, gst_base_sink_get_render_delay (sink)); - break; - case PROP_THROTTLE_TIME: - g_value_set_uint64 (value, gst_base_sink_get_throttle_time (sink)); - break; - case PROP_MAX_BITRATE: - g_value_set_uint64 (value, gst_base_sink_get_max_bitrate (sink)); - break; - case PROP_PROCESSING_DEADLINE: - g_value_set_uint64 (value, gst_base_sink_get_processing_deadline (sink)); - break; - case PROP_STATS: - g_value_take_boxed (value, gst_base_sink_get_stats (sink)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - - -static GstCaps * -gst_base_sink_default_get_caps (GstBaseSink * sink, GstCaps * filter) -{ - return NULL; -} - -static gboolean -gst_base_sink_default_set_caps (GstBaseSink * sink, GstCaps * caps) -{ - return TRUE; -} - -/* with PREROLL_LOCK, STREAM_LOCK */ -static gboolean -gst_base_sink_commit_state (GstBaseSink * basesink) -{ - /* commit state and proceed to next pending state */ - GstState current, next, pending, post_pending; - gboolean post_paused = FALSE; - gboolean post_async_done = FALSE; - gboolean post_playing = FALSE; - - /* we are certainly not playing async anymore now */ - basesink->playing_async = FALSE; - - GST_OBJECT_LOCK (basesink); - current = GST_STATE (basesink); - next = GST_STATE_NEXT (basesink); - pending = GST_STATE_PENDING (basesink); - post_pending = pending; - - switch (pending) { - case GST_STATE_PLAYING: - { - GST_DEBUG_OBJECT (basesink, "committing state to PLAYING"); - - basesink->need_preroll = FALSE; - post_async_done = TRUE; - basesink->priv->committed = TRUE; - post_playing = TRUE; - /* post PAUSED too when we were READY */ - if (current == GST_STATE_READY) { - post_paused = TRUE; - } - break; - } - case GST_STATE_PAUSED: - GST_DEBUG_OBJECT (basesink, "committing state to PAUSED"); - post_paused = TRUE; - post_async_done = TRUE; - basesink->priv->committed = TRUE; - post_pending = GST_STATE_VOID_PENDING; - break; - case GST_STATE_READY: - case GST_STATE_NULL: - goto stopping; - case GST_STATE_VOID_PENDING: - goto nothing_pending; - default: - break; - } - - /* we can report latency queries now */ - basesink->priv->have_latency = TRUE; - - GST_STATE (basesink) = pending; - GST_STATE_NEXT (basesink) = GST_STATE_VOID_PENDING; - GST_STATE_PENDING (basesink) = GST_STATE_VOID_PENDING; - GST_STATE_RETURN (basesink) = GST_STATE_CHANGE_SUCCESS; - GST_OBJECT_UNLOCK (basesink); - - if (post_paused) { - GST_DEBUG_OBJECT (basesink, "posting PAUSED state change message"); - gst_element_post_message (GST_ELEMENT_CAST (basesink), - gst_message_new_state_changed (GST_OBJECT_CAST (basesink), - current, next, post_pending)); - } - if (post_async_done) { - GST_DEBUG_OBJECT (basesink, "posting async-done message"); - gst_element_post_message (GST_ELEMENT_CAST (basesink), - gst_message_new_async_done (GST_OBJECT_CAST (basesink), - GST_CLOCK_TIME_NONE)); - } - if (post_playing) { - if (post_paused) { - GstElementClass *klass; - - klass = GST_ELEMENT_GET_CLASS (basesink); - basesink->have_preroll = TRUE; - /* after releasing this lock, the state change function - * can execute concurrently with this thread. There is nothing we do to - * prevent this for now. subclasses should be prepared to handle it. */ - GST_BASE_SINK_PREROLL_UNLOCK (basesink); - - if (klass->change_state) - klass->change_state (GST_ELEMENT_CAST (basesink), - GST_STATE_CHANGE_PAUSED_TO_PLAYING); - - GST_BASE_SINK_PREROLL_LOCK (basesink); - /* state change function could have been executed and we could be - * flushing now */ - if (G_UNLIKELY (basesink->flushing)) - goto stopping_unlocked; - } - GST_DEBUG_OBJECT (basesink, "posting PLAYING state change message"); - /* FIXME, we released the PREROLL lock above, it's possible that this - * message is not correct anymore when the element went back to PAUSED */ - gst_element_post_message (GST_ELEMENT_CAST (basesink), - gst_message_new_state_changed (GST_OBJECT_CAST (basesink), - next, pending, GST_STATE_VOID_PENDING)); - } - - gst_element_post_message (GST_ELEMENT_CAST (basesink), - gst_message_new_latency (GST_OBJECT_CAST (basesink))); - - GST_STATE_BROADCAST (basesink); - - return TRUE; - -nothing_pending: - { - /* Depending on the state, set our vars. We get in this situation when the - * state change function got a change to update the state vars before the - * streaming thread did. This is fine but we need to make sure that we - * update the need_preroll var since it was %TRUE when we got here and might - * become %FALSE if we got to PLAYING. */ - GST_DEBUG_OBJECT (basesink, "nothing to commit, now in %s", - gst_element_state_get_name (current)); - switch (current) { - case GST_STATE_PLAYING: - basesink->need_preroll = FALSE; - break; - case GST_STATE_PAUSED: - basesink->need_preroll = TRUE; - break; - default: - basesink->need_preroll = FALSE; - basesink->flushing = TRUE; - break; - } - /* we can report latency queries now */ - basesink->priv->have_latency = TRUE; - GST_OBJECT_UNLOCK (basesink); - - gst_element_post_message (GST_ELEMENT_CAST (basesink), - gst_message_new_latency (GST_OBJECT_CAST (basesink))); - return TRUE; - } -stopping_unlocked: - { - GST_OBJECT_LOCK (basesink); - goto stopping; - } -stopping: - { - /* app is going to READY */ - GST_DEBUG_OBJECT (basesink, "stopping"); - basesink->need_preroll = FALSE; - basesink->flushing = TRUE; - GST_OBJECT_UNLOCK (basesink); - return FALSE; - } -} - -static void -start_stepping (GstBaseSink * sink, GstSegment * segment, - GstStepInfo * pending, GstStepInfo * current) -{ - gint64 end; - GstMessage *message; - - GST_DEBUG_OBJECT (sink, "update pending step"); - - GST_OBJECT_LOCK (sink); - memcpy (current, pending, sizeof (GstStepInfo)); - pending->valid = FALSE; - GST_OBJECT_UNLOCK (sink); - - /* post message first */ - message = - gst_message_new_step_start (GST_OBJECT (sink), TRUE, current->format, - current->amount, current->rate, current->flush, current->intermediate); - gst_message_set_seqnum (message, current->seqnum); - gst_element_post_message (GST_ELEMENT (sink), message); - - /* get the running time of where we paused and remember it */ - current->start = gst_element_get_start_time (GST_ELEMENT_CAST (sink)); - gst_segment_set_running_time (segment, GST_FORMAT_TIME, current->start); - - /* set the new rate for the remainder of the segment */ - current->start_rate = segment->rate; - segment->rate *= current->rate; - - /* save values */ - if (segment->rate > 0.0) - current->start_stop = segment->stop; - else - current->start_start = segment->start; - - if (current->format == GST_FORMAT_TIME) { - /* calculate the running-time when the step operation should stop */ - if (current->amount != -1) - end = current->start + current->amount; - else - end = -1; - - if (!current->flush) { - gint64 position; - - /* update the segment clipping regions for non-flushing seeks */ - if (segment->rate > 0.0) { - if (end != -1) - position = - gst_segment_position_from_running_time (segment, GST_FORMAT_TIME, - end); - else - position = segment->stop; - - segment->stop = position; - segment->position = position; - } else { - if (end != -1) - position = - gst_segment_position_from_running_time (segment, GST_FORMAT_TIME, - end); - else - position = segment->start; - - segment->time = position; - segment->start = position; - segment->position = position; - } - } - } - - GST_DEBUG_OBJECT (sink, "segment now %" GST_SEGMENT_FORMAT, segment); - GST_DEBUG_OBJECT (sink, "step started at running_time %" GST_TIME_FORMAT, - GST_TIME_ARGS (current->start)); - - GST_DEBUG_OBJECT (sink, "step amount: %" G_GUINT64_FORMAT ", format: %s, " - "rate: %f", current->amount, gst_format_get_name (current->format), - current->rate); -} - -static void -stop_stepping (GstBaseSink * sink, GstSegment * segment, - GstStepInfo * current, gint64 rstart, gint64 rstop, gboolean eos) -{ - gint64 stop, position; - GstMessage *message; - - GST_DEBUG_OBJECT (sink, "step complete"); - - if (segment->rate > 0.0) - stop = rstart; - else - stop = rstop; - - GST_DEBUG_OBJECT (sink, - "step stop at running_time %" GST_TIME_FORMAT, GST_TIME_ARGS (stop)); - - if (stop == -1) - current->duration = current->position; - else - current->duration = stop - current->start; - - GST_DEBUG_OBJECT (sink, "step elapsed running_time %" GST_TIME_FORMAT, - GST_TIME_ARGS (current->duration)); - - position = current->start + current->duration; - - /* now move the segment to the new running time */ - gst_segment_set_running_time (segment, GST_FORMAT_TIME, position); - - if (current->flush) { - /* and remove the time we flushed, start time did not change */ - segment->base = current->start; - } else { - /* start time is now the stepped position */ - gst_element_set_start_time (GST_ELEMENT_CAST (sink), position); - } - - /* restore the previous rate */ - segment->rate = current->start_rate; - - if (segment->rate > 0.0) - segment->stop = current->start_stop; - else - segment->start = current->start_start; - - /* post the step done when we know the stepped duration in TIME */ - message = - gst_message_new_step_done (GST_OBJECT_CAST (sink), current->format, - current->amount, current->rate, current->flush, current->intermediate, - current->duration, eos); - gst_message_set_seqnum (message, current->seqnum); - gst_element_post_message (GST_ELEMENT_CAST (sink), message); - - if (!current->intermediate) - sink->need_preroll = current->need_preroll; - - /* and the current step info finished and becomes invalid */ - current->valid = FALSE; -} - -static gboolean -handle_stepping (GstBaseSink * sink, GstSegment * segment, - GstStepInfo * current, guint64 * cstart, guint64 * cstop, guint64 * rstart, - guint64 * rstop) -{ - gboolean step_end = FALSE; - - /* stepping never stops */ - if (current->amount == -1) - return FALSE; - - /* see if we need to skip this buffer because of stepping */ - switch (current->format) { - case GST_FORMAT_TIME: - { - guint64 end; - guint64 first, last; - gdouble abs_rate; - - if (segment->rate > 0.0) { - if (segment->stop == *cstop) - *rstop = *rstart + current->amount; - - first = *rstart; - last = *rstop; - } else { - if (segment->start == *cstart) - *rstart = *rstop + current->amount; - - first = *rstop; - last = *rstart; - } - - end = current->start + current->amount; - current->position = first - current->start; - - abs_rate = ABS (segment->rate); - if (G_UNLIKELY (abs_rate != 1.0)) - current->position /= abs_rate; - - GST_DEBUG_OBJECT (sink, - "buffer: %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT, - GST_TIME_ARGS (first), GST_TIME_ARGS (last)); - GST_DEBUG_OBJECT (sink, - "got time step %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT "/%" - GST_TIME_FORMAT, GST_TIME_ARGS (current->position), - GST_TIME_ARGS (last - current->start), - GST_TIME_ARGS (current->amount)); - - if ((current->flush && current->position >= current->amount) - || last >= end) { - GST_DEBUG_OBJECT (sink, "step ended, we need clipping"); - step_end = TRUE; - if (segment->rate > 0.0) { - *rstart = end; - *cstart = - gst_segment_position_from_running_time (segment, GST_FORMAT_TIME, - end); - } else { - *rstop = end; - *cstop = - gst_segment_position_from_running_time (segment, GST_FORMAT_TIME, - end); - } - } - GST_DEBUG_OBJECT (sink, - "cstart %" GST_TIME_FORMAT ", rstart %" GST_TIME_FORMAT, - GST_TIME_ARGS (*cstart), GST_TIME_ARGS (*rstart)); - GST_DEBUG_OBJECT (sink, - "cstop %" GST_TIME_FORMAT ", rstop %" GST_TIME_FORMAT, - GST_TIME_ARGS (*cstop), GST_TIME_ARGS (*rstop)); - break; - } - case GST_FORMAT_BUFFERS: - GST_DEBUG_OBJECT (sink, - "got default step %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, - current->position, current->amount); - - if (current->position < current->amount) { - current->position++; - } else { - step_end = TRUE; - } - break; - case GST_FORMAT_DEFAULT: - default: - GST_DEBUG_OBJECT (sink, - "got unknown step %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, - current->position, current->amount); - break; - } - return step_end; -} - -/* with STREAM_LOCK, PREROLL_LOCK - * - * Returns %TRUE if the object needs synchronisation and takes therefore - * part in prerolling. - * - * rsstart/rsstop contain the start/stop in stream time. - * rrstart/rrstop contain the start/stop in running time. - */ -static gboolean -gst_base_sink_get_sync_times (GstBaseSink * basesink, GstMiniObject * obj, - GstClockTime * rsstart, GstClockTime * rsstop, - GstClockTime * rrstart, GstClockTime * rrstop, GstClockTime * rrnext, - gboolean * do_sync, gboolean * stepped, GstStepInfo * step, - gboolean * step_end) -{ - GstBaseSinkClass *bclass; - GstClockTime start, stop; /* raw start/stop timestamps */ - guint64 cstart, cstop; /* clipped raw timestamps */ - guint64 rstart, rstop, rnext; /* clipped timestamps converted to running time */ - GstClockTime sstart, sstop; /* clipped timestamps converted to stream time */ - GstFormat format; - GstBaseSinkPrivate *priv; - GstSegment *segment; - gboolean eos; - - priv = basesink->priv; - segment = &basesink->segment; - - bclass = GST_BASE_SINK_GET_CLASS (basesink); - -again: - /* start with nothing */ - start = stop = GST_CLOCK_TIME_NONE; - eos = FALSE; - - if (G_UNLIKELY (GST_IS_EVENT (obj))) { - GstEvent *event = GST_EVENT_CAST (obj); - - switch (GST_EVENT_TYPE (event)) { - /* EOS event needs syncing */ - case GST_EVENT_EOS: - { - if (segment->rate >= 0.0) { - sstart = sstop = priv->current_sstop; - if (!GST_CLOCK_TIME_IS_VALID (sstart)) { - /* we have not seen a buffer yet, use the segment values */ - sstart = sstop = gst_segment_to_stream_time (segment, - segment->format, segment->stop); - } - } else { - sstart = sstop = priv->current_sstart; - if (!GST_CLOCK_TIME_IS_VALID (sstart)) { - /* we have not seen a buffer yet, use the segment values */ - sstart = sstop = gst_segment_to_stream_time (segment, - segment->format, segment->start); - } - } - - rstart = rstop = rnext = priv->eos_rtime; - *do_sync = GST_CLOCK_TIME_IS_VALID (rstart); - GST_DEBUG_OBJECT (basesink, "sync times for EOS %" GST_TIME_FORMAT, - GST_TIME_ARGS (rstart)); - /* if we are stepping, we end now */ - *step_end = step->valid; - eos = TRUE; - goto eos_done; - } - case GST_EVENT_GAP: - { - GstClockTime timestamp, duration; - gst_event_parse_gap (event, ×tamp, &duration); - - GST_DEBUG_OBJECT (basesink, "Got Gap time %" GST_TIME_FORMAT - " duration %" GST_TIME_FORMAT, - GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration)); - - if (GST_CLOCK_TIME_IS_VALID (timestamp)) { - start = timestamp; - if (GST_CLOCK_TIME_IS_VALID (duration)) - stop = start + duration; - } - *do_sync = TRUE; - break; - } - default: - /* other events do not need syncing */ - return FALSE; - } - } else { - /* else do buffer sync code */ - GstBuffer *buffer = GST_BUFFER_CAST (obj); - - /* just get the times to see if we need syncing, if the retuned start is -1 - * we don't sync. */ - if (bclass->get_times) - bclass->get_times (basesink, buffer, &start, &stop); - - if (!GST_CLOCK_TIME_IS_VALID (start)) { - /* we don't need to sync but we still want to get the timestamps for - * tracking the position */ - gst_base_sink_default_get_times (basesink, buffer, &start, &stop); - *do_sync = FALSE; - } else { - *do_sync = TRUE; - } - } - - GST_DEBUG_OBJECT (basesink, "got times start: %" GST_TIME_FORMAT - ", stop: %" GST_TIME_FORMAT ", do_sync %d", GST_TIME_ARGS (start), - GST_TIME_ARGS (stop), *do_sync); - - /* collect segment and format for code clarity */ - format = segment->format; - - /* clip */ - if (G_UNLIKELY (!gst_segment_clip (segment, format, - start, stop, &cstart, &cstop))) { - if (step->valid) { - GST_DEBUG_OBJECT (basesink, "step out of segment"); - /* when we are stepping, pretend we're at the end of the segment */ - if (segment->rate > 0.0) { - cstart = segment->stop; - cstop = segment->stop; - } else { - cstart = segment->start; - cstop = segment->start; - } - goto do_times; - } - goto out_of_segment; - } - - if (G_UNLIKELY (start != cstart || stop != cstop)) { - GST_DEBUG_OBJECT (basesink, "clipped to: start %" GST_TIME_FORMAT - ", stop: %" GST_TIME_FORMAT, GST_TIME_ARGS (cstart), - GST_TIME_ARGS (cstop)); - } - - /* set last stop position */ - if (G_LIKELY (stop != GST_CLOCK_TIME_NONE && cstop != GST_CLOCK_TIME_NONE)) - segment->position = cstop; - else - segment->position = cstart; - -do_times: - rstart = gst_segment_to_running_time (segment, format, cstart); - rstop = gst_segment_to_running_time (segment, format, cstop); - - /* In reverse playback, play from stop to start */ - if (segment->rate < 0.0 && GST_CLOCK_TIME_IS_VALID (rstop) - /* FIXME: Current stepping implemenation expects unmodified rstart/rstop - * for reverse playback. Don't swap those values when stepping - * unless stepping code is updated as such */ - && !step->valid) { - GstClockTime tmp = rstart; - rstart = rstop; - rstop = tmp; - } - - if (GST_CLOCK_TIME_IS_VALID (stop)) - rnext = rstop; - else - rnext = rstart; - - if (G_UNLIKELY (step->valid)) { - if (!(*step_end = handle_stepping (basesink, segment, step, &cstart, &cstop, - &rstart, &rstop))) { - /* step is still busy, we discard data when we are flushing */ - *stepped = step->flush; - GST_DEBUG_OBJECT (basesink, "stepping busy"); - } - } - /* this can produce wrong values if we accumulated non-TIME segments. If this happens, - * upstream is behaving very badly */ - sstart = gst_segment_to_stream_time (segment, format, cstart); - sstop = gst_segment_to_stream_time (segment, format, cstop); - -eos_done: - /* eos_done label only called when doing EOS, we also stop stepping then */ - if (*step_end && step->flush) { - GST_DEBUG_OBJECT (basesink, "flushing step ended"); - stop_stepping (basesink, segment, step, rstart, rstop, eos); - *step_end = FALSE; - /* re-determine running start times for adjusted segment - * (which has a flushed amount of running/accumulated time removed) */ - if (!GST_IS_EVENT (obj)) { - GST_DEBUG_OBJECT (basesink, "refresh sync times"); - goto again; - } - } - - /* save times */ - *rsstart = sstart; - *rsstop = sstop; - *rrstart = rstart; - *rrstop = rstop; - *rrnext = rnext; - - /* buffers and EOS always need syncing and preroll */ - return TRUE; - - /* special cases */ -out_of_segment: - { - /* we usually clip in the chain function already but stepping could cause - * the segment to be updated later. we return %FALSE so that we don't try - * to sync on it. */ - GST_LOG_OBJECT (basesink, "buffer skipped, not in segment"); - return FALSE; - } -} - -/* with STREAM_LOCK, PREROLL_LOCK, LOCK - * adjust a timestamp with the latency and timestamp offset. This function does - * not adjust for the render delay. */ -static GstClockTime -gst_base_sink_adjust_time (GstBaseSink * basesink, GstClockTime time) -{ - GstClockTimeDiff ts_offset; - - /* don't do anything funny with invalid timestamps */ - if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) - return time; - - time += basesink->priv->latency; - - /* apply offset, be careful for underflows */ - ts_offset = basesink->priv->ts_offset; - if (ts_offset < 0) { - ts_offset = -ts_offset; - if (ts_offset < time) - time -= ts_offset; - else - time = 0; - } else - time += ts_offset; - - /* subtract the render delay again, which was included in the latency */ - if (time > basesink->priv->render_delay) - time -= basesink->priv->render_delay; - else - time = 0; - - return time; -} - -/** - * gst_base_sink_wait_clock: - * @sink: the sink - * @time: the running_time to be reached - * @jitter: (out) (allow-none): the jitter to be filled with time diff, or %NULL - * - * This function will block until @time is reached. It is usually called by - * subclasses that use their own internal synchronisation. - * - * If @time is not valid, no synchronisation is done and %GST_CLOCK_BADTIME is - * returned. Likewise, if synchronisation is disabled in the element or there - * is no clock, no synchronisation is done and %GST_CLOCK_BADTIME is returned. - * - * This function should only be called with the PREROLL_LOCK held, like when - * receiving an EOS event in the #GstBaseSinkClass::event vmethod or when - * receiving a buffer in - * the #GstBaseSinkClass::render vmethod. - * - * The @time argument should be the running_time of when this method should - * return and is not adjusted with any latency or offset configured in the - * sink. - * - * Returns: #GstClockReturn - */ -GstClockReturn -gst_base_sink_wait_clock (GstBaseSink * sink, GstClockTime time, - GstClockTimeDiff * jitter) -{ - GstClockReturn ret; - GstClock *clock; - GstClockTime base_time; - - if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) - goto invalid_time; - - GST_OBJECT_LOCK (sink); - if (G_UNLIKELY (!sink->sync)) - goto no_sync; - - if (G_UNLIKELY ((clock = GST_ELEMENT_CLOCK (sink)) == NULL)) - goto no_clock; - - base_time = GST_ELEMENT_CAST (sink)->base_time; - GST_LOG_OBJECT (sink, - "time %" GST_TIME_FORMAT ", base_time %" GST_TIME_FORMAT, - GST_TIME_ARGS (time), GST_TIME_ARGS (base_time)); - - /* add base_time to running_time to get the time against the clock */ - time += base_time; - - /* Re-use existing clockid if available */ - if (G_LIKELY (sink->priv->cached_clock_id != NULL - && gst_clock_id_uses_clock (sink->priv->cached_clock_id, clock))) { - if (!gst_clock_single_shot_id_reinit (clock, sink->priv->cached_clock_id, - time)) { - gst_clock_id_unref (sink->priv->cached_clock_id); - sink->priv->cached_clock_id = gst_clock_new_single_shot_id (clock, time); - } - } else { - if (sink->priv->cached_clock_id != NULL) - gst_clock_id_unref (sink->priv->cached_clock_id); - sink->priv->cached_clock_id = gst_clock_new_single_shot_id (clock, time); - } - GST_OBJECT_UNLOCK (sink); - - /* A blocking wait is performed on the clock. We save the ClockID - * so we can unlock the entry at any time. While we are blocking, we - * release the PREROLL_LOCK so that other threads can interrupt the - * entry. */ - sink->clock_id = sink->priv->cached_clock_id; - /* release the preroll lock while waiting */ - GST_BASE_SINK_PREROLL_UNLOCK (sink); - - ret = gst_clock_id_wait (sink->priv->cached_clock_id, jitter); - - GST_BASE_SINK_PREROLL_LOCK (sink); - sink->clock_id = NULL; - - return ret; - - /* no syncing needed */ -invalid_time: - { - GST_DEBUG_OBJECT (sink, "time not valid, no sync needed"); - return GST_CLOCK_BADTIME; - } -no_sync: - { - GST_DEBUG_OBJECT (sink, "sync disabled"); - GST_OBJECT_UNLOCK (sink); - return GST_CLOCK_BADTIME; - } -no_clock: - { - GST_DEBUG_OBJECT (sink, "no clock, can't sync"); - GST_OBJECT_UNLOCK (sink); - return GST_CLOCK_BADTIME; - } -} - -/** - * gst_base_sink_wait_preroll: - * @sink: the sink - * - * If the #GstBaseSinkClass::render method performs its own synchronisation - * against the clock it must unblock when going from PLAYING to the PAUSED state - * and call this method before continuing to render the remaining data. - * - * If the #GstBaseSinkClass::render method can block on something else than - * the clock, it must also be ready to unblock immediately on - * the #GstBaseSinkClass::unlock method and cause the - * #GstBaseSinkClass::render method to immediately call this function. - * In this case, the subclass must be prepared to continue rendering where it - * left off if this function returns %GST_FLOW_OK. - * - * This function will block until a state change to PLAYING happens (in which - * case this function returns %GST_FLOW_OK) or the processing must be stopped due - * to a state change to READY or a FLUSH event (in which case this function - * returns %GST_FLOW_FLUSHING). - * - * This function should only be called with the PREROLL_LOCK held, like in the - * render function. - * - * Returns: %GST_FLOW_OK if the preroll completed and processing can - * continue. Any other return value should be returned from the render vmethod. - */ -GstFlowReturn -gst_base_sink_wait_preroll (GstBaseSink * sink) -{ - sink->have_preroll = TRUE; - GST_DEBUG_OBJECT (sink, "waiting in preroll for flush or PLAYING"); - /* block until the state changes, or we get a flush, or something */ - GST_BASE_SINK_PREROLL_WAIT (sink); - sink->have_preroll = FALSE; - if (G_UNLIKELY (sink->flushing)) - goto stopping; - if (G_UNLIKELY (sink->priv->step_unlock)) - goto step_unlocked; - GST_DEBUG_OBJECT (sink, "continue after preroll"); - - return GST_FLOW_OK; - - /* ERRORS */ -stopping: - { - GST_DEBUG_OBJECT (sink, "preroll interrupted because of flush"); - return GST_FLOW_FLUSHING; - } -step_unlocked: - { - sink->priv->step_unlock = FALSE; - GST_DEBUG_OBJECT (sink, "preroll interrupted because of step"); - return GST_FLOW_STEP; - } -} - -/** - * gst_base_sink_do_preroll: - * @sink: the sink - * @obj: (transfer none): the mini object that caused the preroll - * - * If the @sink spawns its own thread for pulling buffers from upstream it - * should call this method after it has pulled a buffer. If the element needed - * to preroll, this function will perform the preroll and will then block - * until the element state is changed. - * - * This function should be called with the PREROLL_LOCK held. - * - * Returns: %GST_FLOW_OK if the preroll completed and processing can - * continue. Any other return value should be returned from the render vmethod. - */ -GstFlowReturn -gst_base_sink_do_preroll (GstBaseSink * sink, GstMiniObject * obj) -{ - GstFlowReturn ret; - - while (G_UNLIKELY (sink->need_preroll)) { - GST_DEBUG_OBJECT (sink, "prerolling object %p", obj); - - /* if it's a buffer, we need to call the preroll method */ - if (sink->priv->call_preroll) { - GstBaseSinkClass *bclass; - GstBuffer *buf; - - if (GST_IS_BUFFER_LIST (obj)) { - buf = gst_buffer_list_get (GST_BUFFER_LIST_CAST (obj), 0); - gst_base_sink_set_last_buffer (sink, buf); - gst_base_sink_set_last_buffer_list (sink, GST_BUFFER_LIST_CAST (obj)); - g_assert (NULL != buf); - } else if (GST_IS_BUFFER (obj)) { - buf = GST_BUFFER_CAST (obj); - /* For buffer lists do not set last buffer for now */ - gst_base_sink_set_last_buffer (sink, buf); - gst_base_sink_set_last_buffer_list (sink, NULL); - } else { - buf = NULL; - } - - if (buf) { - GST_DEBUG_OBJECT (sink, "preroll buffer %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); - - bclass = GST_BASE_SINK_GET_CLASS (sink); - - if (bclass->prepare) - if ((ret = bclass->prepare (sink, buf)) != GST_FLOW_OK) - goto prepare_canceled; - - if (bclass->preroll) - if ((ret = bclass->preroll (sink, buf)) != GST_FLOW_OK) - goto preroll_canceled; - - sink->priv->call_preroll = FALSE; - } - } - - /* commit state */ - if (G_LIKELY (sink->playing_async)) { - if (G_UNLIKELY (!gst_base_sink_commit_state (sink))) - goto stopping; - } - - /* need to recheck here because the commit state could have - * made us not need the preroll anymore */ - if (G_LIKELY (sink->need_preroll)) { - /* block until the state changes, or we get a flush, or something */ - ret = gst_base_sink_wait_preroll (sink); - if ((ret != GST_FLOW_OK) && (ret != GST_FLOW_STEP)) - goto preroll_failed; - } - } - return GST_FLOW_OK; - - /* ERRORS */ -prepare_canceled: - { - GST_DEBUG_OBJECT (sink, "prepare failed, abort state"); - gst_element_abort_state (GST_ELEMENT_CAST (sink)); - return ret; - } -preroll_canceled: - { - GST_DEBUG_OBJECT (sink, "preroll failed, abort state"); - gst_element_abort_state (GST_ELEMENT_CAST (sink)); - return ret; - } -stopping: - { - GST_DEBUG_OBJECT (sink, "stopping while committing state"); - return GST_FLOW_FLUSHING; - } -preroll_failed: - { - GST_DEBUG_OBJECT (sink, "preroll failed: %s", gst_flow_get_name (ret)); - return ret; - } -} - -/** - * gst_base_sink_wait: - * @sink: the sink - * @time: the running_time to be reached - * @jitter: (out) (allow-none): the jitter to be filled with time diff, or %NULL - * - * This function will wait for preroll to complete and will then block until @time - * is reached. It is usually called by subclasses that use their own internal - * synchronisation but want to let some synchronization (like EOS) be handled - * by the base class. - * - * This function should only be called with the PREROLL_LOCK held (like when - * receiving an EOS event in the ::event vmethod or when handling buffers in - * ::render). - * - * The @time argument should be the running_time of when the timeout should happen - * and will be adjusted with any latency and offset configured in the sink. - * - * Returns: #GstFlowReturn - */ -GstFlowReturn -gst_base_sink_wait (GstBaseSink * sink, GstClockTime time, - GstClockTimeDiff * jitter) -{ - GstClockReturn status; - GstFlowReturn ret; - - do { - GstClockTime stime; - - GST_DEBUG_OBJECT (sink, "checking preroll"); - - /* first wait for the playing state before we can continue */ - while (G_UNLIKELY (sink->need_preroll)) { - ret = gst_base_sink_wait_preroll (sink); - if ((ret != GST_FLOW_OK) && (ret != GST_FLOW_STEP)) - goto flushing; - } - - /* preroll done, we can sync since we are in PLAYING now. */ - GST_DEBUG_OBJECT (sink, "possibly waiting for clock to reach %" - GST_TIME_FORMAT, GST_TIME_ARGS (time)); - - /* compensate for latency, ts_offset and render delay */ - stime = gst_base_sink_adjust_time (sink, time); - - /* wait for the clock, this can be interrupted because we got shut down or - * we PAUSED. */ - status = gst_base_sink_wait_clock (sink, stime, jitter); - - GST_DEBUG_OBJECT (sink, "clock returned %d", status); - - /* invalid time, no clock or sync disabled, just continue then */ - if (status == GST_CLOCK_BADTIME) - break; - - /* waiting could have been interrupted and we can be flushing now */ - if (G_UNLIKELY (sink->flushing)) - goto flushing; - - /* retry if we got unscheduled, which means we did not reach the timeout - * yet. if some other error occurs, we continue. */ - } while (status == GST_CLOCK_UNSCHEDULED); - - GST_DEBUG_OBJECT (sink, "end of stream"); - - return GST_FLOW_OK; - - /* ERRORS */ -flushing: - { - GST_DEBUG_OBJECT (sink, "we are flushing"); - return GST_FLOW_FLUSHING; - } -} - -/* with STREAM_LOCK, PREROLL_LOCK - * - * Make sure we are in PLAYING and synchronize an object to the clock. - * - * If we need preroll, we are not in PLAYING. We try to commit the state - * if needed and then block if we still are not PLAYING. - * - * We start waiting on the clock in PLAYING. If we got interrupted, we - * immediately try to re-preroll. - * - * Some objects do not need synchronisation (most events) and so this function - * immediately returns GST_FLOW_OK. - * - * for objects that arrive later than max-lateness to be synchronized to the - * clock have the @late boolean set to %TRUE. - * - * This function keeps a running average of the jitter (the diff between the - * clock time and the requested sync time). The jitter is negative for - * objects that arrive in time and positive for late buffers. - * - * does not take ownership of obj. - */ -static GstFlowReturn -gst_base_sink_do_sync (GstBaseSink * basesink, - GstMiniObject * obj, gboolean * late, gboolean * step_end) -{ - GstClockTimeDiff jitter = 0; - gboolean syncable; - GstClockReturn status = GST_CLOCK_OK; - GstClockTime rstart, rstop, rnext, sstart, sstop, stime; - gboolean do_sync; - GstBaseSinkPrivate *priv; - GstFlowReturn ret; - GstStepInfo *current, *pending; - gboolean stepped; - guint32 current_instant_rate_seqnum; - - priv = basesink->priv; - - /* remember the currently handled instant-rate sequence number. If this - * changes after pre-rolling, we need to goto do_step again for updating - * the timing information of the current buffer */ - current_instant_rate_seqnum = priv->instant_rate_sync_seqnum; -do_step: - sstart = sstop = rstart = rstop = rnext = GST_CLOCK_TIME_NONE; - do_sync = TRUE; - stepped = FALSE; - - priv->current_rstart = GST_CLOCK_TIME_NONE; - - /* get stepping info */ - current = &priv->current_step; - pending = &priv->pending_step; - - /* get timing information for this object against the render segment */ - syncable = gst_base_sink_get_sync_times (basesink, obj, - &sstart, &sstop, &rstart, &rstop, &rnext, &do_sync, &stepped, current, - step_end); - - if (G_UNLIKELY (stepped)) - goto step_skipped; - - /* a syncable object needs to participate in preroll and - * clocking. All buffers and EOS are syncable. */ - if (G_UNLIKELY (!syncable)) - goto not_syncable; - - /* store timing info for current object */ - priv->current_rstart = rstart; - priv->current_rstop = (GST_CLOCK_TIME_IS_VALID (rstop) ? rstop : rstart); - - /* save sync time for eos when the previous object needed sync */ - priv->eos_rtime = (do_sync ? rnext : GST_CLOCK_TIME_NONE); - - /* calculate inter frame spacing */ - if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (priv->prev_rstart) && - priv->prev_rstart < rstart)) { - GstClockTime in_diff; - - in_diff = rstart - priv->prev_rstart; - - if (priv->avg_in_diff == -1) - priv->avg_in_diff = in_diff; - else - priv->avg_in_diff = UPDATE_RUNNING_AVG (priv->avg_in_diff, in_diff); - - GST_LOG_OBJECT (basesink, "avg frame diff %" GST_TIME_FORMAT, - GST_TIME_ARGS (priv->avg_in_diff)); - - } - priv->prev_rstart = rstart; - - if (G_UNLIKELY (GST_CLOCK_TIME_IS_VALID (priv->earliest_in_time) && - rstart < priv->earliest_in_time)) - goto qos_dropped; - -again: - /* first do preroll, this makes sure we commit our state - * to PAUSED and can continue to PLAYING. We cannot perform - * any clock sync in PAUSED because there is no clock. */ - ret = gst_base_sink_do_preroll (basesink, obj); - if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto preroll_failed; - - /* update the segment with a pending step if the current one is invalid and we - * have a new pending one. We only accept new step updates after a preroll */ - if (G_UNLIKELY (pending->valid && !current->valid)) { - start_stepping (basesink, &basesink->segment, pending, current); - goto do_step; - } - - if (G_UNLIKELY (priv->instant_rate_sync_seqnum != - current_instant_rate_seqnum)) { - current_instant_rate_seqnum = priv->instant_rate_sync_seqnum; - // TODO rename the goto label - it does more these days. - goto do_step; - } - - /* After rendering we store the position of the last buffer so that we can use - * it to report the position. We need to take the lock here. */ - GST_OBJECT_LOCK (basesink); - priv->current_sstart = sstart; - priv->current_sstop = (GST_CLOCK_TIME_IS_VALID (sstop) ? sstop : sstart); - GST_OBJECT_UNLOCK (basesink); - - if (!do_sync) - goto done; - - /* adjust for latency */ - stime = gst_base_sink_adjust_time (basesink, rstart); - - /* adjust for rate control */ - if (priv->rc_next == -1 || (stime != -1 && stime >= priv->rc_next)) { - GST_DEBUG_OBJECT (basesink, "reset rc_time to time %" GST_TIME_FORMAT, - GST_TIME_ARGS (stime)); - priv->rc_time = stime; - priv->rc_accumulated = 0; - } else { - GST_DEBUG_OBJECT (basesink, "rate control next %" GST_TIME_FORMAT, - GST_TIME_ARGS (priv->rc_next)); - stime = priv->rc_next; - } - - /* preroll done, we can sync since we are in PLAYING now. */ - GST_DEBUG_OBJECT (basesink, "possibly waiting for clock to reach %" - GST_TIME_FORMAT ", adjusted %" GST_TIME_FORMAT, - GST_TIME_ARGS (rstart), GST_TIME_ARGS (stime)); - - /* This function will return immediately if start == -1, no clock - * or sync is disabled with GST_CLOCK_BADTIME. */ - status = gst_base_sink_wait_clock (basesink, stime, &jitter); - - GST_DEBUG_OBJECT (basesink, "clock returned %d, jitter %c%" GST_TIME_FORMAT, - status, (jitter < 0 ? '-' : ' '), GST_TIME_ARGS (ABS (jitter))); - - /* invalid time, no clock or sync disabled, just render */ - if (status == GST_CLOCK_BADTIME) - goto done; - - /* waiting could have been interrupted and we can be flushing now */ - if (G_UNLIKELY (basesink->flushing)) - goto flushing; - - /* check for unlocked by a state change, we are not flushing so - * we can try to preroll on the current buffer. */ - if (G_UNLIKELY (status == GST_CLOCK_UNSCHEDULED)) { - if (G_UNLIKELY (priv->instant_rate_sync_seqnum != - current_instant_rate_seqnum)) { - current_instant_rate_seqnum = priv->instant_rate_sync_seqnum; - // TODO rename - goto do_step; - } - - GST_DEBUG_OBJECT (basesink, "unscheduled, waiting some more"); - priv->call_preroll = TRUE; - goto again; - } - - /* successful syncing done, record observation */ - priv->current_jitter = jitter; - - /* check if the object should be dropped */ - *late = gst_base_sink_is_too_late (basesink, obj, rstart, rstop, - status, jitter, TRUE); - -done: - return GST_FLOW_OK; - - /* ERRORS */ -step_skipped: - { - GST_DEBUG_OBJECT (basesink, "skipped stepped object %p", obj); - *late = TRUE; - return GST_FLOW_OK; - } -not_syncable: - { - GST_DEBUG_OBJECT (basesink, "non syncable object %p", obj); - return GST_FLOW_OK; - } -qos_dropped: - { - GST_DEBUG_OBJECT (basesink, "dropped because of QoS %p", obj); - *late = TRUE; - return GST_FLOW_OK; - } -flushing: - { - GST_DEBUG_OBJECT (basesink, "we are flushing"); - return GST_FLOW_FLUSHING; - } -preroll_failed: - { - GST_DEBUG_OBJECT (basesink, "preroll failed"); - *step_end = FALSE; - return ret; - } -} - -static gboolean -gst_base_sink_send_qos (GstBaseSink * basesink, GstQOSType type, - gdouble proportion, GstClockTime time, GstClockTimeDiff diff) -{ - GstEvent *event; - gboolean res; - - /* generate Quality-of-Service event */ - GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink, - "qos: type %d, proportion: %lf, diff %" G_GINT64_FORMAT ", timestamp %" - GST_TIME_FORMAT, type, proportion, diff, GST_TIME_ARGS (time)); - - /* Compensate for any instant-rate-change related running time offset - * between upstream and the internal running time of the sink */ - if (basesink->priv->instant_rate_sync_seqnum != GST_SEQNUM_INVALID) { - GstClockTime actual_duration; - GstClockTime upstream_duration; - GstClockTimeDiff difference; - gboolean negative_duration; - - GST_DEBUG_OBJECT (basesink, - "Current internal running time %" GST_TIME_FORMAT - ", last internal running time %" GST_TIME_FORMAT, GST_TIME_ARGS (time), - GST_TIME_ARGS (basesink->priv->last_anchor_running_time)); - - /* Calculate how much running time was spent since the last switch/segment - * in the "corrected upstream segment", our segment */ - /* Due to rounding errors and other inaccuracies, it can happen - * that our calculated internal running time is before the upstream - * running time. We need to compensate for that */ - if (time < basesink->priv->last_anchor_running_time) { - actual_duration = basesink->priv->last_anchor_running_time - time; - negative_duration = TRUE; - } else { - actual_duration = time - basesink->priv->last_anchor_running_time; - negative_duration = FALSE; - } - - /* Transpose that duration (i.e. what upstream beliefs) */ - upstream_duration = - (actual_duration * basesink->segment.rate) / - basesink->priv->upstream_segment.rate; - - /* Add the difference to the previously accumulated correction */ - if (negative_duration) - difference = upstream_duration - actual_duration; - else - difference = actual_duration - upstream_duration; - - GST_DEBUG_OBJECT (basesink, - "Current instant rate correction offset. Actual duration %" - GST_TIME_FORMAT ", upstream duration %" GST_TIME_FORMAT - ", negative %d, difference %" GST_STIME_FORMAT ", current offset %" - GST_STIME_FORMAT, GST_TIME_ARGS (actual_duration), - GST_TIME_ARGS (upstream_duration), negative_duration, - GST_STIME_ARGS (difference), - GST_STIME_ARGS (basesink->priv->instant_rate_offset + difference)); - - difference = basesink->priv->instant_rate_offset + difference; - - if (difference > 0 && time < difference) - time = 0; - else - time -= difference; - } - - event = gst_event_new_qos (type, proportion, diff, time); - - /* send upstream */ - res = gst_pad_push_event (basesink->sinkpad, event); - - return res; -} - -static void -gst_base_sink_perform_qos (GstBaseSink * sink, gboolean dropped) -{ - GstBaseSinkPrivate *priv; - GstClockTime start, stop; - GstClockTimeDiff jitter; - GstClockTime pt, entered, left; - GstClockTime duration; - gdouble rate; - - priv = sink->priv; - - start = priv->current_rstart; - - if (priv->current_step.valid) - return; - - /* if Quality-of-Service disabled, do nothing */ - if (!g_atomic_int_get (&priv->qos_enabled) || - !GST_CLOCK_TIME_IS_VALID (start)) - return; - - stop = priv->current_rstop; - jitter = priv->current_jitter; - - if (jitter < 0) { - /* this is the time the buffer entered the sink */ - if (start < -jitter) - entered = 0; - else - entered = start + jitter; - left = start; - } else { - /* this is the time the buffer entered the sink */ - entered = start + jitter; - /* this is the time the buffer left the sink */ - left = start + jitter; - } - - /* calculate duration of the buffer, only use buffer durations if not in - * trick mode or key-unit mode. Otherwise the buffer durations will be - * meaningless as frames are being dropped in-between without updating the - * durations. */ - duration = priv->avg_in_diff; - - /* if we have the time when the last buffer left us, calculate - * processing time */ - if (GST_CLOCK_TIME_IS_VALID (priv->last_left)) { - if (entered > priv->last_left) { - pt = entered - priv->last_left; - } else { - pt = 0; - } - } else { - pt = priv->avg_pt; - } - - GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, sink, "start: %" GST_TIME_FORMAT - ", stop %" GST_TIME_FORMAT ", entered %" GST_TIME_FORMAT ", left %" - GST_TIME_FORMAT ", pt: %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT - ",jitter %" G_GINT64_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (stop), - GST_TIME_ARGS (entered), GST_TIME_ARGS (left), GST_TIME_ARGS (pt), - GST_TIME_ARGS (duration), jitter); - - GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, sink, - "avg_pt: %" GST_TIME_FORMAT ", avg_rate: %g", - GST_TIME_ARGS (priv->avg_pt), priv->avg_rate); - - /* collect running averages. for first observations, we copy the - * values */ - if (!GST_CLOCK_TIME_IS_VALID (priv->avg_pt)) - priv->avg_pt = pt; - else - priv->avg_pt = UPDATE_RUNNING_AVG (priv->avg_pt, pt); - - if (duration != -1 && duration != 0) { - rate = - gst_guint64_to_gdouble (priv->avg_pt) / - gst_guint64_to_gdouble (duration); - } else { - rate = 1.0; - } - - if (GST_CLOCK_TIME_IS_VALID (priv->last_left)) { - if (dropped || priv->avg_rate < 0.0) { - priv->avg_rate = rate; - } else { - if (rate > 1.0) - priv->avg_rate = UPDATE_RUNNING_AVG_N (priv->avg_rate, rate); - else - priv->avg_rate = UPDATE_RUNNING_AVG_P (priv->avg_rate, rate); - } - } - - GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, sink, - "updated: avg_pt: %" GST_TIME_FORMAT - ", avg_rate: %g", GST_TIME_ARGS (priv->avg_pt), priv->avg_rate); - - - if (priv->avg_rate >= 0.0) { - GstQOSType type; - GstClockTimeDiff diff; - - /* if we have a valid rate, start sending QoS messages */ - if (priv->current_jitter < 0) { - /* make sure we never go below 0 when adding the jitter to the - * timestamp. */ - if (priv->current_rstart < -priv->current_jitter) - priv->current_jitter = -priv->current_rstart; - } - - if (priv->throttle_time > 0) { - diff = priv->throttle_time; - type = GST_QOS_TYPE_THROTTLE; - } else { - diff = priv->current_jitter; - if (diff <= 0) - type = GST_QOS_TYPE_OVERFLOW; - else - type = GST_QOS_TYPE_UNDERFLOW; - } - - gst_base_sink_send_qos (sink, type, priv->avg_rate, priv->current_rstart, - diff); - } - - /* record when this buffer will leave us */ - priv->last_left = left; -} - -/* reset all qos measuring */ -static void -gst_base_sink_reset_qos (GstBaseSink * sink) -{ - GstBaseSinkPrivate *priv; - - priv = sink->priv; - - priv->last_render_time = GST_CLOCK_TIME_NONE; - priv->prev_rstart = GST_CLOCK_TIME_NONE; - priv->earliest_in_time = GST_CLOCK_TIME_NONE; - priv->last_left = GST_CLOCK_TIME_NONE; - priv->avg_pt = GST_CLOCK_TIME_NONE; - priv->avg_rate = -1.0; - priv->avg_in_diff = GST_CLOCK_TIME_NONE; - priv->rendered = 0; - priv->dropped = 0; - -} - -/* Checks if the object was scheduled too late. - * - * rstart/rstop contain the running_time start and stop values - * of the object. - * - * status and jitter contain the return values from the clock wait. - * - * returns %TRUE if the buffer was too late. - */ -static gboolean -gst_base_sink_is_too_late (GstBaseSink * basesink, GstMiniObject * obj, - GstClockTime rstart, GstClockTime rstop, - GstClockReturn status, GstClockTimeDiff jitter, gboolean render) -{ - gboolean late; - guint64 max_lateness; - GstBaseSinkPrivate *priv; - - priv = basesink->priv; - - late = FALSE; - - /* only for objects that were too late */ - if (G_LIKELY (status != GST_CLOCK_EARLY)) - goto in_time; - - max_lateness = basesink->max_lateness; - - /* check if frame dropping is enabled */ - if (max_lateness == -1) - goto no_drop; - - /* only check for buffers */ - if (G_UNLIKELY (!GST_IS_BUFFER (obj))) - goto not_buffer; - - /* can't do check if we don't have a timestamp */ - if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (rstart))) - goto no_timestamp; - - /* we can add a valid stop time */ - if (GST_CLOCK_TIME_IS_VALID (rstop)) - max_lateness += rstop; - else { - max_lateness += rstart; - /* no stop time, use avg frame diff */ - if (priv->avg_in_diff != -1) - max_lateness += priv->avg_in_diff; - } - - /* if the jitter bigger than duration and lateness we are too late */ - if ((late = rstart + jitter > max_lateness)) { - GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, basesink, - "buffer is too late %" GST_TIME_FORMAT - " > %" GST_TIME_FORMAT, GST_TIME_ARGS (rstart + jitter), - GST_TIME_ARGS (max_lateness)); - /* !!emergency!!, if we did not receive anything valid for more than a - * second, render it anyway so the user sees something */ - if (GST_CLOCK_TIME_IS_VALID (priv->last_render_time) && - rstart - priv->last_render_time > GST_SECOND) { - late = FALSE; - GST_ELEMENT_WARNING (basesink, CORE, CLOCK, - (_("A lot of buffers are being dropped.")), - ("There may be a timestamping problem, or this computer is too slow.")); - GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, basesink, - "**emergency** last buffer at %" GST_TIME_FORMAT " > GST_SECOND", - GST_TIME_ARGS (priv->last_render_time)); - } - } - -done: - if (render && (!late || !GST_CLOCK_TIME_IS_VALID (priv->last_render_time))) { - priv->last_render_time = rstart; - /* the next allowed input timestamp */ - if (priv->throttle_time > 0) - priv->earliest_in_time = rstart + priv->throttle_time; - } - return late; - - /* all is fine */ -in_time: - { - GST_DEBUG_OBJECT (basesink, "object was scheduled in time"); - goto done; - } -no_drop: - { - GST_DEBUG_OBJECT (basesink, "frame dropping disabled"); - goto done; - } -not_buffer: - { - GST_DEBUG_OBJECT (basesink, "object is not a buffer"); - return FALSE; - } -no_timestamp: - { - GST_DEBUG_OBJECT (basesink, "buffer has no timestamp"); - return FALSE; - } -} - -static void -gst_base_sink_update_start_time (GstBaseSink * basesink) -{ - GstClock *clock; - - GST_OBJECT_LOCK (basesink); - if (GST_STATE (basesink) == GST_STATE_PLAYING - && (clock = GST_ELEMENT_CLOCK (basesink))) { - GstClockTime now; - - gst_object_ref (clock); - GST_OBJECT_UNLOCK (basesink); - - /* calculate the time when we stopped */ - now = gst_clock_get_time (clock); - gst_object_unref (clock); - - GST_OBJECT_LOCK (basesink); - /* store the current running time */ - if (GST_ELEMENT_START_TIME (basesink) != GST_CLOCK_TIME_NONE) { - if (now != GST_CLOCK_TIME_NONE) - GST_ELEMENT_START_TIME (basesink) = - now - GST_ELEMENT_CAST (basesink)->base_time; - else - GST_WARNING_OBJECT (basesink, - "Clock %s returned invalid time, can't calculate " - "running_time when going to the PAUSED state", - GST_OBJECT_NAME (clock)); - } - GST_DEBUG_OBJECT (basesink, - "start_time=%" GST_TIME_FORMAT ", now=%" GST_TIME_FORMAT - ", base_time %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_ELEMENT_START_TIME (basesink)), - GST_TIME_ARGS (now), - GST_TIME_ARGS (GST_ELEMENT_CAST (basesink)->base_time)); - } - GST_OBJECT_UNLOCK (basesink); -} - -static void -gst_base_sink_flush_start (GstBaseSink * basesink, GstPad * pad) -{ - /* make sure we are not blocked on the clock also clear any pending - * eos state. */ - gst_base_sink_set_flushing (basesink, pad, TRUE); - - /* we grab the stream lock but that is not needed since setting the - * sink to flushing would make sure no state commit is being done - * anymore */ - GST_PAD_STREAM_LOCK (pad); - gst_base_sink_reset_qos (basesink); - /* and we need to commit our state again on the next - * prerolled buffer */ - basesink->playing_async = TRUE; - if (basesink->priv->async_enabled) { - gst_base_sink_update_start_time (basesink); - gst_element_lost_state (GST_ELEMENT_CAST (basesink)); - } else { - /* start time reset in above case as well; - * arranges for a.o. proper position reporting when flushing in PAUSED */ - gst_element_set_start_time (GST_ELEMENT_CAST (basesink), 0); - basesink->priv->have_latency = TRUE; - } - gst_base_sink_set_last_buffer (basesink, NULL); - gst_base_sink_set_last_buffer_list (basesink, NULL); - GST_PAD_STREAM_UNLOCK (pad); -} - -static void -gst_base_sink_flush_stop (GstBaseSink * basesink, GstPad * pad, - gboolean reset_time) -{ - /* unset flushing so we can accept new data, this also flushes out any EOS - * event. */ - gst_base_sink_set_flushing (basesink, pad, FALSE); - - /* for position reporting */ - GST_OBJECT_LOCK (basesink); - basesink->priv->current_sstart = GST_CLOCK_TIME_NONE; - basesink->priv->current_sstop = GST_CLOCK_TIME_NONE; - basesink->priv->eos_rtime = GST_CLOCK_TIME_NONE; - basesink->priv->call_preroll = TRUE; - basesink->priv->current_step.valid = FALSE; - basesink->priv->pending_step.valid = FALSE; - if (basesink->pad_mode == GST_PAD_MODE_PUSH) { - /* we need new segment info after the flush. */ - basesink->have_newsegment = FALSE; - if (reset_time) { - gst_segment_init (&basesink->priv->upstream_segment, - GST_FORMAT_UNDEFINED); - gst_segment_init (&basesink->segment, GST_FORMAT_UNDEFINED); - GST_ELEMENT_START_TIME (basesink) = 0; - - basesink->priv->last_instant_rate_seqnum = GST_SEQNUM_INVALID; - basesink->priv->instant_rate_sync_seqnum = GST_SEQNUM_INVALID; - basesink->priv->instant_rate_multiplier = 0; - basesink->priv->segment_seqnum = GST_SEQNUM_INVALID; - basesink->priv->instant_rate_offset = 0; - basesink->priv->last_anchor_running_time = 0; - } - } - GST_OBJECT_UNLOCK (basesink); - - if (reset_time) { - GST_DEBUG_OBJECT (basesink, "posting reset-time message"); - gst_element_post_message (GST_ELEMENT_CAST (basesink), - gst_message_new_reset_time (GST_OBJECT_CAST (basesink), 0)); - } -} - -static GstFlowReturn -gst_base_sink_default_wait_event (GstBaseSink * basesink, GstEvent * event) -{ - GstFlowReturn ret; - gboolean late, step_end = FALSE; - - ret = gst_base_sink_do_sync (basesink, GST_MINI_OBJECT_CAST (event), - &late, &step_end); - - return ret; -} - -static GstFlowReturn -gst_base_sink_wait_event (GstBaseSink * basesink, GstEvent * event) -{ - GstFlowReturn ret; - GstBaseSinkClass *bclass; - - bclass = GST_BASE_SINK_GET_CLASS (basesink); - - if (G_LIKELY (bclass->wait_event)) - ret = bclass->wait_event (basesink, event); - else - ret = GST_FLOW_NOT_SUPPORTED; - - return ret; -} - -static gboolean -gst_base_sink_default_event (GstBaseSink * basesink, GstEvent * event) -{ - gboolean result = TRUE; - GstBaseSinkClass *bclass; - - bclass = GST_BASE_SINK_GET_CLASS (basesink); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_START: - { - GST_DEBUG_OBJECT (basesink, "flush-start %p", event); - gst_base_sink_flush_start (basesink, basesink->sinkpad); - break; - } - case GST_EVENT_FLUSH_STOP: - { - gboolean reset_time; - - gst_event_parse_flush_stop (event, &reset_time); - GST_DEBUG_OBJECT (basesink, "flush-stop %p, reset_time: %d", event, - reset_time); - gst_base_sink_flush_stop (basesink, basesink->sinkpad, reset_time); - break; - } - case GST_EVENT_EOS: - { - GstMessage *message; - guint32 seqnum; - - /* we set the received EOS flag here so that we can use it when testing if - * we are prerolled and to refuse more buffers. */ - basesink->priv->received_eos = TRUE; - - /* wait for EOS */ - if (G_UNLIKELY (gst_base_sink_wait_event (basesink, - event) != GST_FLOW_OK)) { - result = FALSE; - goto done; - } - - /* the EOS event is completely handled so we mark - * ourselves as being in the EOS state. eos is also - * protected by the object lock so we can read it when - * answering the POSITION query. */ - GST_OBJECT_LOCK (basesink); - basesink->eos = TRUE; - GST_OBJECT_UNLOCK (basesink); - - /* ok, now we can post the message */ - GST_DEBUG_OBJECT (basesink, "Now posting EOS"); - - seqnum = basesink->priv->seqnum = gst_event_get_seqnum (event); - GST_DEBUG_OBJECT (basesink, "Got seqnum #%" G_GUINT32_FORMAT, seqnum); - - message = gst_message_new_eos (GST_OBJECT_CAST (basesink)); - gst_message_set_seqnum (message, seqnum); - gst_element_post_message (GST_ELEMENT_CAST (basesink), message); - break; - } - case GST_EVENT_STREAM_START: - { - GstMessage *message; - guint32 seqnum; - guint group_id; - - seqnum = gst_event_get_seqnum (event); - GST_DEBUG_OBJECT (basesink, "Now posting STREAM_START (seqnum:%d)", - seqnum); - message = gst_message_new_stream_start (GST_OBJECT_CAST (basesink)); - if (gst_event_parse_group_id (event, &group_id)) { - gst_message_set_group_id (message, group_id); - } else { - GST_FIXME_OBJECT (basesink, "stream-start event without group-id. " - "Consider implementing group-id handling in the upstream " - "elements"); - } - gst_message_set_seqnum (message, seqnum); - gst_element_post_message (GST_ELEMENT_CAST (basesink), message); - break; - } - case GST_EVENT_CAPS: - { - GstCaps *caps, *current_caps; - - GST_DEBUG_OBJECT (basesink, "caps %p", event); - - gst_event_parse_caps (event, &caps); - current_caps = gst_pad_get_current_caps (GST_BASE_SINK_PAD (basesink)); - - if (current_caps && gst_caps_is_equal (current_caps, caps)) { - GST_DEBUG_OBJECT (basesink, - "New caps equal to old ones: %" GST_PTR_FORMAT, caps); - } else { - if (bclass->set_caps) - result = bclass->set_caps (basesink, caps); - - if (result) { - GST_OBJECT_LOCK (basesink); - gst_caps_replace (&basesink->priv->caps, caps); - GST_OBJECT_UNLOCK (basesink); - } - } - if (current_caps) - gst_caps_unref (current_caps); - break; - } - case GST_EVENT_SEGMENT:{ - guint32 seqnum = gst_event_get_seqnum (event); - GstSegment new_segment; - - /* configure the segment */ - /* The segment is protected with both the STREAM_LOCK and the OBJECT_LOCK. - * We protect with the OBJECT_LOCK so that we can use the values to - * safely answer a POSITION query. */ - GST_OBJECT_LOCK (basesink); - /* the new segment event is needed to bring the buffer timestamps to the - * stream time and to drop samples outside of the playback segment. */ - - gst_event_copy_segment (event, &new_segment); - - GST_DEBUG_OBJECT (basesink, - "received upstream segment %u %" GST_SEGMENT_FORMAT, seqnum, - &new_segment); - - /* Make sure that the position stays between start and stop */ - new_segment.position = - CLAMP (new_segment.position, new_segment.start, new_segment.stop); - - if (basesink->priv->instant_rate_sync_seqnum != GST_SEQNUM_INVALID) { - GstClockTime upstream_duration; - GstClockTime actual_duration; - GstClockTime new_segment_running_time; - GstClockTimeDiff difference; - gboolean negative_duration; - - /* Calculate how much running time upstream believes has passed since - * the last switch/segment */ - new_segment_running_time = - gst_segment_to_running_time (&new_segment, GST_FORMAT_TIME, - new_segment.position); - - GST_DEBUG_OBJECT (basesink, - "Current upstream running time %" GST_TIME_FORMAT - ", last upstream running time %" GST_TIME_FORMAT, - GST_TIME_ARGS (new_segment_running_time), - GST_TIME_ARGS (basesink->priv->last_anchor_running_time - - basesink->priv->instant_rate_offset)); - - /* Due to rounding errors and other inaccuracies, it can happen - * that our calculated internal running time is before the upstream - * running time. We need to compensate for that */ - if (new_segment_running_time < - basesink->priv->last_anchor_running_time - - basesink->priv->instant_rate_offset) { - upstream_duration = - basesink->priv->last_anchor_running_time - - basesink->priv->instant_rate_offset - new_segment_running_time; - negative_duration = TRUE; - } else { - upstream_duration = - new_segment_running_time - - basesink->priv->last_anchor_running_time + - basesink->priv->instant_rate_offset; - negative_duration = FALSE; - } - - /* Calculate the actual running-time duration of the previous segment */ - actual_duration = - (upstream_duration * basesink->priv->instant_rate_multiplier); - - if (negative_duration) - difference = upstream_duration - actual_duration; - else - difference = actual_duration - upstream_duration; - - GST_DEBUG_OBJECT (basesink, - "Current internal running time %" GST_TIME_FORMAT - ", last internal running time %" GST_TIME_FORMAT, - GST_TIME_ARGS (new_segment_running_time + - basesink->priv->instant_rate_offset + difference), - GST_TIME_ARGS (basesink->priv->last_anchor_running_time)); - - /* Add the difference to the previously accumulated correction. */ - basesink->priv->instant_rate_offset += difference; - - GST_DEBUG_OBJECT (basesink, - "Updating instant rate correction offset. Actual duration %" - GST_TIME_FORMAT ", upstream duration %" GST_TIME_FORMAT - ", negative %d, difference %" GST_STIME_FORMAT ", new offset %" - GST_STIME_FORMAT, GST_TIME_ARGS (actual_duration), - GST_TIME_ARGS (upstream_duration), - negative_duration, - GST_STIME_ARGS (difference), - GST_STIME_ARGS (basesink->priv->instant_rate_offset)); - - if (basesink->priv->instant_rate_offset < 0 && - new_segment_running_time < -basesink->priv->instant_rate_offset) { - GST_WARNING_OBJECT (basesink, - "Upstream current running time %" GST_TIME_FORMAT - " is smaller than calculated offset %" GST_STIME_FORMAT, - GST_TIME_ARGS (new_segment_running_time), - GST_STIME_ARGS (basesink->priv->instant_rate_offset)); - - basesink->priv->last_anchor_running_time = 0; - basesink->priv->instant_rate_offset = 0; - } else { - basesink->priv->last_anchor_running_time = - new_segment_running_time + basesink->priv->instant_rate_offset; - } - - /* Update the segments from the event and with the newly calculated - * correction offset */ - basesink->priv->upstream_segment = new_segment; - basesink->segment = new_segment; - - basesink->segment.rate *= basesink->priv->instant_rate_multiplier; - - gst_segment_offset_running_time (&basesink->segment, GST_FORMAT_TIME, - basesink->priv->instant_rate_offset); - - GST_DEBUG_OBJECT (basesink, - "Adjusted segment is now %" GST_SEGMENT_FORMAT, &basesink->segment); - } else { - /* otherwise both segments are simply the same, no correction needed */ - basesink->priv->upstream_segment = new_segment; - basesink->segment = new_segment; - basesink->priv->last_anchor_running_time = - gst_segment_to_running_time (&new_segment, new_segment.format, - new_segment.position); - basesink->priv->instant_rate_offset = 0; /* Should already be 0, but to be sure */ - } - - GST_DEBUG_OBJECT (basesink, "configured segment %" GST_SEGMENT_FORMAT, - &basesink->segment); - basesink->priv->segment_seqnum = seqnum; - basesink->have_newsegment = TRUE; - gst_base_sink_reset_qos (basesink); - GST_OBJECT_UNLOCK (basesink); - break; - } - case GST_EVENT_GAP: - { - if (G_UNLIKELY (gst_base_sink_wait_event (basesink, - event) != GST_FLOW_OK)) - result = FALSE; - break; - } - case GST_EVENT_TAG: - { - GstTagList *taglist; - - gst_event_parse_tag (event, &taglist); - - gst_element_post_message (GST_ELEMENT_CAST (basesink), - gst_message_new_tag (GST_OBJECT_CAST (basesink), - gst_tag_list_copy (taglist))); - break; - } - case GST_EVENT_TOC: - { - GstToc *toc; - gboolean updated; - - gst_event_parse_toc (event, &toc, &updated); - - gst_element_post_message (GST_ELEMENT_CAST (basesink), - gst_message_new_toc (GST_OBJECT_CAST (basesink), toc, updated)); - - gst_toc_unref (toc); - break; - } - case GST_EVENT_SINK_MESSAGE: - { - GstMessage *msg = NULL; - - gst_event_parse_sink_message (event, &msg); - if (msg) - gst_element_post_message (GST_ELEMENT_CAST (basesink), msg); - break; - } - case GST_EVENT_INSTANT_RATE_CHANGE: - { - GstMessage *msg; - gdouble rate_multiplier; - guint32 seqnum = gst_event_get_seqnum (event); - - GST_OBJECT_LOCK (basesink); - if (G_UNLIKELY (basesink->priv->last_instant_rate_seqnum == seqnum)) { - /* Ignore repeated event */ - GST_LOG_OBJECT (basesink, - "Ignoring repeated instant-rate-change event"); - GST_OBJECT_UNLOCK (basesink); - break; - } - if (basesink->priv->instant_rate_sync_seqnum == seqnum) { - /* Ignore if we already received the instant-rate-sync-time event from the pipeline */ - GST_LOG_OBJECT (basesink, - "Ignoring instant-rate-change event for which we already received instant-rate-sync-time"); - GST_OBJECT_UNLOCK (basesink); - break; - } - - basesink->priv->last_instant_rate_seqnum = seqnum; - GST_OBJECT_UNLOCK (basesink); - - gst_event_parse_instant_rate_change (event, &rate_multiplier, NULL); - - msg = - gst_message_new_instant_rate_request (GST_OBJECT_CAST (basesink), - rate_multiplier); - gst_message_set_seqnum (msg, seqnum); - gst_element_post_message (GST_ELEMENT_CAST (basesink), msg); - - break; - } - default: - break; - } -done: - gst_event_unref (event); - - return result; -} - -static gboolean -gst_base_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) -{ - GstBaseSink *basesink; - gboolean result = TRUE; - GstBaseSinkClass *bclass; - - basesink = GST_BASE_SINK_CAST (parent); - bclass = GST_BASE_SINK_GET_CLASS (basesink); - - GST_DEBUG_OBJECT (basesink, "received event %p %" GST_PTR_FORMAT, event, - event); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_STOP: - /* special case for this serialized event because we don't want to grab - * the PREROLL lock or check if we were flushing */ - if (bclass->event) - result = bclass->event (basesink, event); - break; - default: - if (GST_EVENT_IS_SERIALIZED (event)) { - GST_BASE_SINK_PREROLL_LOCK (basesink); - if (G_UNLIKELY (basesink->flushing)) - goto flushing; - - if (G_UNLIKELY (basesink->priv->received_eos)) - goto after_eos; - - if (bclass->event) - result = bclass->event (basesink, event); - - GST_BASE_SINK_PREROLL_UNLOCK (basesink); - } else { - if (bclass->event) - result = bclass->event (basesink, event); - } - break; - } -done: - return result; - - /* ERRORS */ -flushing: - { - GST_DEBUG_OBJECT (basesink, "we are flushing"); - GST_BASE_SINK_PREROLL_UNLOCK (basesink); - gst_event_unref (event); - result = FALSE; - goto done; - } - -after_eos: - { - GST_DEBUG_OBJECT (basesink, "Event received after EOS, dropping"); - GST_BASE_SINK_PREROLL_UNLOCK (basesink); - gst_event_unref (event); - result = FALSE; - goto done; - } -} - -/* default implementation to calculate the start and end - * timestamps on a buffer, subclasses can override - */ -static void -gst_base_sink_default_get_times (GstBaseSink * basesink, GstBuffer * buffer, - GstClockTime * start, GstClockTime * end) -{ - GstClockTime timestamp, duration; - - /* first sync on DTS, else use PTS */ - timestamp = GST_BUFFER_DTS (buffer); - if (!GST_CLOCK_TIME_IS_VALID (timestamp)) - timestamp = GST_BUFFER_PTS (buffer); - - if (GST_CLOCK_TIME_IS_VALID (timestamp)) { - /* get duration to calculate end time */ - duration = GST_BUFFER_DURATION (buffer); - if (GST_CLOCK_TIME_IS_VALID (duration)) { - *end = timestamp + duration; - } - *start = timestamp; - } -} - -/* must be called with PREROLL_LOCK */ -static gboolean -gst_base_sink_needs_preroll (GstBaseSink * basesink) -{ - gboolean is_prerolled, res; - - /* we have 2 cases where the PREROLL_LOCK is released: - * 1) we are blocking in the PREROLL_LOCK and thus are prerolled. - * 2) we are syncing on the clock - */ - is_prerolled = basesink->have_preroll || basesink->priv->received_eos; - res = !is_prerolled; - - GST_DEBUG_OBJECT (basesink, "have_preroll: %d, EOS: %d => needs preroll: %d", - basesink->have_preroll, basesink->priv->received_eos, res); - - return res; -} - -/* with STREAM_LOCK, PREROLL_LOCK - * - * Takes a buffer and compare the timestamps with the last segment. - * If the buffer falls outside of the segment boundaries, drop it. - * Else send the buffer for preroll and rendering. - * - * This function takes ownership of the buffer. - */ -static GstFlowReturn -gst_base_sink_chain_unlocked (GstBaseSink * basesink, GstPad * pad, - gpointer obj, gboolean is_list) -{ - GstBaseSinkClass *bclass; - GstBaseSinkPrivate *priv = basesink->priv; - GstFlowReturn ret = GST_FLOW_OK; - GstClockTime start = GST_CLOCK_TIME_NONE, end = GST_CLOCK_TIME_NONE; - GstSegment *segment; - GstBuffer *sync_buf; - gboolean late, step_end, prepared = FALSE; - - if (G_UNLIKELY (basesink->flushing)) - goto flushing; - - if (G_UNLIKELY (priv->received_eos)) - goto was_eos; - - if (is_list) { - GstBufferList *buffer_list = GST_BUFFER_LIST_CAST (obj); - - if (gst_buffer_list_length (buffer_list) == 0) - goto empty_list; - - sync_buf = gst_buffer_list_get (buffer_list, 0); - g_assert (NULL != sync_buf); - } else { - sync_buf = GST_BUFFER_CAST (obj); - } - - /* for code clarity */ - segment = &basesink->segment; - - if (G_UNLIKELY (!basesink->have_newsegment)) { - gboolean sync; - - sync = gst_base_sink_get_sync (basesink); - if (sync) { - GST_ELEMENT_WARNING (basesink, STREAM, FAILED, - (_("Internal data flow problem.")), - ("Received buffer without a new-segment. Assuming timestamps start from 0.")); - } - - /* this means this sink will assume timestamps start from 0 */ - GST_OBJECT_LOCK (basesink); - segment->start = 0; - segment->stop = -1; - basesink->segment.start = 0; - basesink->segment.stop = -1; - basesink->have_newsegment = TRUE; - GST_OBJECT_UNLOCK (basesink); - } - - bclass = GST_BASE_SINK_GET_CLASS (basesink); - - /* check if the buffer needs to be dropped, we first ask the subclass for the - * start and end */ - if (bclass->get_times) - bclass->get_times (basesink, sync_buf, &start, &end); - - if (!GST_CLOCK_TIME_IS_VALID (start)) { - /* if the subclass does not want sync, we use our own values so that we at - * least clip the buffer to the segment */ - gst_base_sink_default_get_times (basesink, sync_buf, &start, &end); - } - - GST_DEBUG_OBJECT (basesink, "got times start: %" GST_TIME_FORMAT - ", end: %" GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (end)); - - /* a dropped buffer does not participate in anything. Buffer can only be - * dropped if their PTS falls completely outside the segment, while we sync - * preferably on DTS */ - if (GST_CLOCK_TIME_IS_VALID (start) && (segment->format == GST_FORMAT_TIME)) { - GstClockTime pts = GST_BUFFER_PTS (sync_buf); - GstClockTime pts_end = GST_CLOCK_TIME_NONE; - - if (!GST_CLOCK_TIME_IS_VALID (pts)) - pts = start; - - if (GST_CLOCK_TIME_IS_VALID (end)) - pts_end = pts + (end - start); - - if (G_UNLIKELY (!gst_segment_clip (segment, - GST_FORMAT_TIME, pts, pts_end, NULL, NULL) - && priv->drop_out_of_segment)) - goto out_of_segment; - } - - if (bclass->prepare || bclass->prepare_list) { - gboolean do_sync = TRUE, stepped = FALSE, syncable = TRUE; - GstClockTime sstart, sstop, rstart, rstop, rnext; - GstStepInfo *current; - - late = FALSE; - step_end = FALSE; - - current = &priv->current_step; - syncable = - gst_base_sink_get_sync_times (basesink, obj, &sstart, &sstop, &rstart, - &rstop, &rnext, &do_sync, &stepped, current, &step_end); - - if (G_UNLIKELY (stepped)) - goto dropped; - - if (syncable && do_sync && gst_base_sink_get_sync (basesink)) { - GstClock *clock; - - GST_OBJECT_LOCK (basesink); - clock = GST_ELEMENT_CLOCK (basesink); - if (clock && GST_STATE (basesink) == GST_STATE_PLAYING) { - GstClockTime base_time; - GstClockTime stime; - GstClockTime now; - - base_time = GST_ELEMENT_CAST (basesink)->base_time; - stime = base_time + gst_base_sink_adjust_time (basesink, rstart); - now = gst_clock_get_time (clock); - GST_OBJECT_UNLOCK (basesink); - - late = - gst_base_sink_is_too_late (basesink, obj, rstart, rstop, - GST_CLOCK_EARLY, GST_CLOCK_DIFF (stime, now), FALSE); - } else { - GST_OBJECT_UNLOCK (basesink); - } - } - - /* We are about to prepare the first frame, make sure we have prerolled - * already. This prevent nesting prepare/render calls. */ - ret = gst_base_sink_do_preroll (basesink, obj); - if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto preroll_failed; - - if (G_UNLIKELY (late)) - goto dropped; - - if (!is_list) { - if (bclass->prepare) { - ret = bclass->prepare (basesink, GST_BUFFER_CAST (obj)); - if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto prepare_failed; - } - } else { - if (bclass->prepare_list) { - ret = bclass->prepare_list (basesink, GST_BUFFER_LIST_CAST (obj)); - if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto prepare_failed; - } - } - - prepared = TRUE; - } - -again: - late = FALSE; - step_end = FALSE; - - /* synchronize this object, non syncable objects return OK - * immediately. */ - ret = gst_base_sink_do_sync (basesink, GST_MINI_OBJECT_CAST (sync_buf), - &late, &step_end); - if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto sync_failed; - - /* Don't skip if prepare() was called on time */ - late = late && !prepared; - - /* drop late buffers unconditionally, let's hope it's unlikely */ - if (G_UNLIKELY (late)) - goto dropped; - - if (priv->max_bitrate) { - gsize size; - - if (is_list) - size = gst_buffer_list_calculate_size (GST_BUFFER_LIST_CAST (obj)); - else - size = gst_buffer_get_size (GST_BUFFER_CAST (obj)); - - priv->rc_accumulated += size; - priv->rc_next = priv->rc_time + gst_util_uint64_scale (priv->rc_accumulated, - 8 * GST_SECOND, priv->max_bitrate); - } - - GST_DEBUG_OBJECT (basesink, "rendering object %p", obj); - - if (!is_list) { - /* For buffer lists do not set last buffer for now. */ - gst_base_sink_set_last_buffer (basesink, GST_BUFFER_CAST (obj)); - gst_base_sink_set_last_buffer_list (basesink, NULL); - - if (bclass->render) - ret = bclass->render (basesink, GST_BUFFER_CAST (obj)); - } else { - GstBufferList *buffer_list = GST_BUFFER_LIST_CAST (obj); - - if (bclass->render_list) - ret = bclass->render_list (basesink, buffer_list); - - /* Set the first buffer and buffer list to be included in last sample */ - gst_base_sink_set_last_buffer (basesink, sync_buf); - gst_base_sink_set_last_buffer_list (basesink, buffer_list); - } - - if (ret == GST_FLOW_STEP) - goto again; - - if (G_UNLIKELY (basesink->flushing)) - goto flushing; - - priv->rendered++; - -done: - if (step_end) { - /* the step ended, check if we need to activate a new step */ - GST_DEBUG_OBJECT (basesink, "step ended"); - stop_stepping (basesink, &basesink->segment, &priv->current_step, - priv->current_rstart, priv->current_rstop, basesink->eos); - goto again; - } - - gst_base_sink_perform_qos (basesink, late); - - GST_DEBUG_OBJECT (basesink, "object unref after render %p", obj); - gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj)); - - return ret; - - /* ERRORS */ -flushing: - { - GST_DEBUG_OBJECT (basesink, "sink is flushing"); - gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj)); - return GST_FLOW_FLUSHING; - } -was_eos: - { - GST_DEBUG_OBJECT (basesink, "we are EOS, dropping object, return EOS"); - gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj)); - return GST_FLOW_EOS; - } -empty_list: - { - GST_DEBUG_OBJECT (basesink, "buffer list with no buffers"); - gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj)); - return GST_FLOW_OK; - } -out_of_segment: - { - GST_DEBUG_OBJECT (basesink, "dropping buffer, out of clipping segment"); - gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj)); - return GST_FLOW_OK; - } -prepare_failed: - { - GST_DEBUG_OBJECT (basesink, "prepare buffer failed %s", - gst_flow_get_name (ret)); - gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj)); - return ret; - } -sync_failed: - { - GST_DEBUG_OBJECT (basesink, "do_sync returned %s", gst_flow_get_name (ret)); - goto done; - } -dropped: - { - priv->dropped++; - GST_DEBUG_OBJECT (basesink, "buffer late, dropping"); - - if (g_atomic_int_get (&priv->qos_enabled)) { - GstMessage *qos_msg; - GstClockTime timestamp, duration; - - timestamp = GST_BUFFER_TIMESTAMP (GST_BUFFER_CAST (sync_buf)); - duration = GST_BUFFER_DURATION (GST_BUFFER_CAST (sync_buf)); - - GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink, - "qos: dropped buffer rt %" GST_TIME_FORMAT ", st %" GST_TIME_FORMAT - ", ts %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT, - GST_TIME_ARGS (priv->current_rstart), - GST_TIME_ARGS (priv->current_sstart), GST_TIME_ARGS (timestamp), - GST_TIME_ARGS (duration)); - GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, basesink, - "qos: rendered %" G_GUINT64_FORMAT ", dropped %" G_GUINT64_FORMAT, - priv->rendered, priv->dropped); - - qos_msg = - gst_message_new_qos (GST_OBJECT_CAST (basesink), basesink->sync, - priv->current_rstart, priv->current_sstart, timestamp, duration); - gst_message_set_qos_values (qos_msg, priv->current_jitter, priv->avg_rate, - 1000000); - gst_message_set_qos_stats (qos_msg, GST_FORMAT_BUFFERS, priv->rendered, - priv->dropped); - gst_element_post_message (GST_ELEMENT_CAST (basesink), qos_msg); - } - goto done; - } -preroll_failed: - { - GST_DEBUG_OBJECT (basesink, "preroll failed: %s", gst_flow_get_name (ret)); - gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj)); - return ret; - } -} - -/* with STREAM_LOCK - */ -static GstFlowReturn -gst_base_sink_chain_main (GstBaseSink * basesink, GstPad * pad, gpointer obj, - gboolean is_list) -{ - GstFlowReturn result; - - if (G_UNLIKELY (basesink->pad_mode != GST_PAD_MODE_PUSH)) - goto wrong_mode; - - GST_BASE_SINK_PREROLL_LOCK (basesink); - result = gst_base_sink_chain_unlocked (basesink, pad, obj, is_list); - GST_BASE_SINK_PREROLL_UNLOCK (basesink); - -done: - return result; - - /* ERRORS */ -wrong_mode: - { - GST_OBJECT_LOCK (pad); - GST_WARNING_OBJECT (basesink, - "Push on pad %s:%s, but it was not activated in push mode", - GST_DEBUG_PAD_NAME (pad)); - GST_OBJECT_UNLOCK (pad); - gst_mini_object_unref (GST_MINI_OBJECT_CAST (obj)); - /* we don't post an error message this will signal to the peer - * pushing that EOS is reached. */ - result = GST_FLOW_EOS; - goto done; - } -} - -static GstFlowReturn -gst_base_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) -{ - GstBaseSink *basesink; - - basesink = GST_BASE_SINK (parent); - - return gst_base_sink_chain_main (basesink, pad, buf, FALSE); -} - -static GstFlowReturn -gst_base_sink_chain_list (GstPad * pad, GstObject * parent, - GstBufferList * list) -{ - GstBaseSink *basesink; - GstBaseSinkClass *bclass; - GstFlowReturn result; - - basesink = GST_BASE_SINK (parent); - bclass = GST_BASE_SINK_GET_CLASS (basesink); - - if (G_LIKELY (bclass->render_list)) { - result = gst_base_sink_chain_main (basesink, pad, list, TRUE); - } else { - guint i, len; - GstBuffer *buffer; - - GST_LOG_OBJECT (pad, "chaining each buffer in list"); - - len = gst_buffer_list_length (list); - - result = GST_FLOW_OK; - for (i = 0; i < len; i++) { - buffer = gst_buffer_list_get (list, i); - result = gst_base_sink_chain_main (basesink, pad, - gst_buffer_ref (buffer), FALSE); - if (result != GST_FLOW_OK) - break; - } - gst_buffer_list_unref (list); - } - return result; -} - - -static gboolean -gst_base_sink_default_do_seek (GstBaseSink * sink, GstSegment * segment) -{ - gboolean res = TRUE; - - /* update our offset if the start/stop position was updated */ - if (segment->format == GST_FORMAT_BYTES) { - segment->time = segment->start; - } else if (segment->start == 0) { - /* seek to start, we can implement a default for this. */ - segment->time = 0; - } else { - res = FALSE; - GST_INFO_OBJECT (sink, "Can't do a default seek"); - } - - return res; -} - -#define SEEK_TYPE_IS_RELATIVE(t) (((t) != GST_SEEK_TYPE_NONE) && ((t) != GST_SEEK_TYPE_SET)) - -static gboolean -gst_base_sink_default_prepare_seek_segment (GstBaseSink * sink, - GstEvent * event, GstSegment * segment) -{ - /* By default, we try one of 2 things: - * - For absolute seek positions, convert the requested position to our - * configured processing format and place it in the output segment \ - * - For relative seek positions, convert our current (input) values to the - * seek format, adjust by the relative seek offset and then convert back to - * the processing format - */ - GstSeekType start_type, stop_type; - gint64 start, stop; - GstSeekFlags flags; - GstFormat seek_format; - gdouble rate; - gboolean update; - gboolean res = TRUE; - - gst_event_parse_seek (event, &rate, &seek_format, &flags, - &start_type, &start, &stop_type, &stop); - - if (seek_format == segment->format) { - gst_segment_do_seek (segment, rate, seek_format, flags, - start_type, start, stop_type, stop, &update); - return TRUE; - } - - if (start_type != GST_SEEK_TYPE_NONE) { - /* FIXME: Handle seek_end by converting the input segment vals */ - res = - gst_pad_query_convert (sink->sinkpad, seek_format, start, - segment->format, &start); - start_type = GST_SEEK_TYPE_SET; - } - - if (res && stop_type != GST_SEEK_TYPE_NONE) { - /* FIXME: Handle seek_end by converting the input segment vals */ - res = - gst_pad_query_convert (sink->sinkpad, seek_format, stop, - segment->format, &stop); - stop_type = GST_SEEK_TYPE_SET; - } - - /* And finally, configure our output segment in the desired format */ - gst_segment_do_seek (segment, rate, segment->format, flags, start_type, start, - stop_type, stop, &update); - - if (!res) - goto no_format; - - return res; - -no_format: - { - GST_DEBUG_OBJECT (sink, "undefined format given, seek aborted."); - return FALSE; - } -} - -/* perform a seek, only executed in pull mode */ -static gboolean -gst_base_sink_perform_seek (GstBaseSink * sink, GstPad * pad, GstEvent * event) -{ - gboolean flush; - gdouble rate; - GstFormat seek_format, dest_format; - GstSeekFlags flags; - GstSeekType start_type, stop_type; - gboolean seekseg_configured = FALSE; - gint64 start, stop; - gboolean update, res = TRUE; - GstSegment seeksegment; - - dest_format = sink->segment.format; - - if (event) { - GST_DEBUG_OBJECT (sink, "performing seek with event %p", event); - gst_event_parse_seek (event, &rate, &seek_format, &flags, - &start_type, &start, &stop_type, &stop); - - flush = flags & GST_SEEK_FLAG_FLUSH; - } else { - GST_DEBUG_OBJECT (sink, "performing seek without event"); - flush = FALSE; - } - - if (flush) { - GST_DEBUG_OBJECT (sink, "flushing upstream"); - gst_pad_push_event (pad, gst_event_new_flush_start ()); - gst_base_sink_flush_start (sink, pad); - } else { - GST_DEBUG_OBJECT (sink, "pausing pulling thread"); - } - - GST_PAD_STREAM_LOCK (pad); - - /* If we configured the seeksegment above, don't overwrite it now. Otherwise - * copy the current segment info into the temp segment that we can actually - * attempt the seek with. We only update the real segment if the seek succeeds. */ - if (!seekseg_configured) { - memcpy (&seeksegment, &sink->segment, sizeof (GstSegment)); - - /* now configure the final seek segment */ - if (event) { - if (sink->segment.format != seek_format) { - /* OK, here's where we give the subclass a chance to convert the relative - * seek into an absolute one in the processing format. We set up any - * absolute seek above, before taking the stream lock. */ - if (!gst_base_sink_default_prepare_seek_segment (sink, event, - &seeksegment)) { - GST_DEBUG_OBJECT (sink, - "Preparing the seek failed after flushing. " "Aborting seek"); - res = FALSE; - } - } else { - /* The seek format matches our processing format, no need to ask the - * the subclass to configure the segment. */ - gst_segment_do_seek (&seeksegment, rate, seek_format, flags, - start_type, start, stop_type, stop, &update); - } - } - /* Else, no seek event passed, so we're just (re)starting the - current segment. */ - } - - if (res) { - GST_DEBUG_OBJECT (sink, "segment configured from %" G_GINT64_FORMAT - " to %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT, - seeksegment.start, seeksegment.stop, seeksegment.position); - - /* do the seek, segment.position contains the new position. */ - res = gst_base_sink_default_do_seek (sink, &seeksegment); - } - - if (flush) { - GST_DEBUG_OBJECT (sink, "stop flushing upstream"); - gst_pad_push_event (pad, gst_event_new_flush_stop (TRUE)); - gst_base_sink_flush_stop (sink, pad, TRUE); - } else if (res && sink->running) { - /* we are running the current segment and doing a non-flushing seek, - * close the segment first based on the position. */ - GST_DEBUG_OBJECT (sink, "closing running segment %" G_GINT64_FORMAT - " to %" G_GINT64_FORMAT, sink->segment.start, sink->segment.position); - } - - /* The subclass must have converted the segment to the processing format - * by now */ - if (res && seeksegment.format != dest_format) { - GST_DEBUG_OBJECT (sink, "Subclass failed to prepare a seek segment " - "in the correct format. Aborting seek."); - res = FALSE; - } - - GST_INFO_OBJECT (sink, "seeking done %d: %" GST_SEGMENT_FORMAT, res, - &seeksegment); - - /* if successful seek, we update our real segment and push - * out the new segment. */ - if (res) { - gst_segment_copy_into (&seeksegment, &sink->segment); - - if (sink->segment.flags & GST_SEGMENT_FLAG_SEGMENT) { - gst_element_post_message (GST_ELEMENT (sink), - gst_message_new_segment_start (GST_OBJECT (sink), - sink->segment.format, sink->segment.position)); - } - } - - sink->priv->discont = TRUE; - sink->running = TRUE; - - GST_PAD_STREAM_UNLOCK (pad); - - return res; -} - -static void -set_step_info (GstBaseSink * sink, GstStepInfo * current, GstStepInfo * pending, - guint seqnum, GstFormat format, guint64 amount, gdouble rate, - gboolean flush, gboolean intermediate) -{ - GST_OBJECT_LOCK (sink); - pending->seqnum = seqnum; - pending->format = format; - pending->amount = amount; - pending->position = 0; - pending->rate = rate; - pending->flush = flush; - pending->intermediate = intermediate; - pending->valid = TRUE; - /* flush invalidates the current stepping segment */ - if (flush) - current->valid = FALSE; - GST_OBJECT_UNLOCK (sink); -} - -static gboolean -gst_base_sink_perform_step (GstBaseSink * sink, GstPad * pad, GstEvent * event) -{ - GstBaseSinkPrivate *priv; - GstBaseSinkClass *bclass; - gboolean flush, intermediate; - gdouble rate; - GstFormat format; - guint64 amount; - guint seqnum; - GstStepInfo *pending, *current; - GstMessage *message; - - bclass = GST_BASE_SINK_GET_CLASS (sink); - priv = sink->priv; - - GST_DEBUG_OBJECT (sink, "performing step with event %p", event); - - gst_event_parse_step (event, &format, &amount, &rate, &flush, &intermediate); - seqnum = gst_event_get_seqnum (event); - - pending = &priv->pending_step; - current = &priv->current_step; - - /* post message first */ - message = gst_message_new_step_start (GST_OBJECT (sink), FALSE, format, - amount, rate, flush, intermediate); - gst_message_set_seqnum (message, seqnum); - gst_element_post_message (GST_ELEMENT (sink), message); - - if (flush) { - /* we need to call ::unlock before locking PREROLL_LOCK - * since we lock it before going into ::render */ - if (bclass->unlock) - bclass->unlock (sink); - - GST_BASE_SINK_PREROLL_LOCK (sink); - /* now that we have the PREROLL lock, clear our unlock request */ - if (bclass->unlock_stop) - bclass->unlock_stop (sink); - - /* update the stepinfo and make it valid */ - set_step_info (sink, current, pending, seqnum, format, amount, rate, flush, - intermediate); - - if (sink->priv->async_enabled) { - /* and we need to commit our state again on the next - * prerolled buffer */ - sink->playing_async = TRUE; - priv->pending_step.need_preroll = TRUE; - sink->need_preroll = FALSE; - gst_base_sink_update_start_time (sink); - gst_element_lost_state (GST_ELEMENT_CAST (sink)); - } else { - sink->priv->have_latency = TRUE; - sink->need_preroll = FALSE; - } - priv->current_sstart = GST_CLOCK_TIME_NONE; - priv->current_sstop = GST_CLOCK_TIME_NONE; - priv->eos_rtime = GST_CLOCK_TIME_NONE; - priv->call_preroll = TRUE; - gst_base_sink_set_last_buffer (sink, NULL); - gst_base_sink_set_last_buffer_list (sink, NULL); - gst_base_sink_reset_qos (sink); - - if (sink->clock_id) { - gst_clock_id_unschedule (sink->clock_id); - } - - if (sink->have_preroll) { - GST_DEBUG_OBJECT (sink, "signal waiter"); - priv->step_unlock = TRUE; - GST_BASE_SINK_PREROLL_SIGNAL (sink); - } - GST_BASE_SINK_PREROLL_UNLOCK (sink); - } else { - /* update the stepinfo and make it valid */ - set_step_info (sink, current, pending, seqnum, format, amount, rate, flush, - intermediate); - } - - return TRUE; -} - -static gboolean -gst_base_sink_perform_instant_rate_change (GstBaseSink * sink, GstPad * pad, - GstEvent * event) -{ - GstBaseSinkPrivate *priv; - guint32 seqnum; - gdouble rate; - GstClockTime running_time, upstream_running_time; - - GstClockTime switch_time; - gint res; - - priv = sink->priv; - - GST_DEBUG_OBJECT (sink, "performing instant-rate-change with event %p", - event); - - seqnum = gst_event_get_seqnum (event); - gst_event_parse_instant_rate_sync_time (event, &rate, &running_time, - &upstream_running_time); - - GST_DEBUG_OBJECT (sink, "instant-rate-change %u %lf at %" GST_TIME_FORMAT - ", upstream %" GST_TIME_FORMAT, - seqnum, rate, GST_TIME_ARGS (running_time), - GST_TIME_ARGS (upstream_running_time)); - - /* Take the preroll lock so we can change the segment. We do not call unlock - * like for stepping as that would cause the PLAYING state to be lost and - * would get us into prerolling again first - * - * FIXME: The below potentially blocks until the chain function returns, but - * the lock is not taken during all waiting operations inside the chain - * function (clock, preroll) so this should be fine in most cases. Only - * problem is if the render() or prepare() functions are waiting themselves! - * - * FIXME: If the subclass is calling gst_base_sink_wait() it will be woken - * up but there is no way for it to update the timestamps, or to report back - * to the base class that it should recalculate the values. The current - * change would not be instantaneous in that case but would wait until the - * next buffer. - */ - GST_BASE_SINK_PREROLL_LOCK (sink); - - /* We can safely change the segment and everything here as we hold the - * PREROLL_LOCK and it is taken for the whole chain function */ - sink->priv->instant_rate_sync_seqnum = seqnum; - sink->priv->instant_rate_multiplier = rate; - sink->priv->instant_rate_offset = running_time - upstream_running_time; - sink->priv->last_anchor_running_time = running_time; - - GST_DEBUG_OBJECT (sink, "Current internal running time %" GST_TIME_FORMAT - ", last internal running time %" GST_TIME_FORMAT, - GST_TIME_ARGS (running_time), - GST_TIME_ARGS (sink->priv->last_anchor_running_time)); - - /* Calculate the current position in the segment and do a seek with the - * new rate. This updates rate, base and offset accordingly */ - res = - gst_segment_position_from_running_time_full (&sink->segment, - GST_FORMAT_TIME, running_time, &switch_time); - - GST_DEBUG_OBJECT (sink, "Before adjustment seg is %" GST_SEGMENT_FORMAT - " new running_time %" GST_TIME_FORMAT - " position %" GST_STIME_FORMAT " res %d", &sink->segment, - GST_TIME_ARGS (running_time), - GST_STIME_ARGS ((GstClockTimeDiff) switch_time), res); - - if (res < 0) { - GST_WARNING_OBJECT (sink, - "Negative position calculated. Can't instant-rate change to there"); - GST_BASE_SINK_PREROLL_UNLOCK (sink); - return TRUE; - } - - sink->segment.position = switch_time; - - /* Calculate new output rate based on upstream value */ - rate *= sink->priv->upstream_segment.rate; - - gst_segment_do_seek (&sink->segment, rate, GST_FORMAT_TIME, - sink->segment.flags & (~GST_SEEK_FLAG_FLUSH) & - GST_SEEK_FLAG_INSTANT_RATE_CHANGE, GST_SEEK_TYPE_NONE, -1, - GST_SEEK_TYPE_NONE, -1, NULL); - - GST_DEBUG_OBJECT (sink, "Adjusted segment is now %" GST_SEGMENT_FORMAT, - &sink->segment); - - priv->current_sstart = GST_CLOCK_TIME_NONE; - priv->current_sstop = GST_CLOCK_TIME_NONE; - priv->eos_rtime = GST_CLOCK_TIME_NONE; - gst_base_sink_reset_qos (sink); - - if (sink->clock_id) { - gst_clock_id_unschedule (sink->clock_id); - } - - if (sink->have_preroll) { - GST_DEBUG_OBJECT (sink, "signal waiter"); - /* TODO: Rename this, and GST_FLOW_STEP */ - priv->step_unlock = TRUE; - GST_BASE_SINK_PREROLL_SIGNAL (sink); - } - - GST_BASE_SINK_PREROLL_UNLOCK (sink); - - return TRUE; -} - -/* with STREAM_LOCK - */ -static void -gst_base_sink_loop (GstPad * pad) -{ - GstObject *parent; - GstBaseSink *basesink; - GstBuffer *buf = NULL; - GstFlowReturn result; - guint blocksize; - guint64 offset; - - parent = GST_OBJECT_PARENT (pad); - basesink = GST_BASE_SINK (parent); - - g_assert (basesink->pad_mode == GST_PAD_MODE_PULL); - - if ((blocksize = basesink->priv->blocksize) == 0) - blocksize = -1; - - offset = basesink->segment.position; - - GST_DEBUG_OBJECT (basesink, "pulling %" G_GUINT64_FORMAT ", %u", - offset, blocksize); - - result = gst_pad_pull_range (pad, offset, blocksize, &buf); - if (G_UNLIKELY (result != GST_FLOW_OK)) - goto paused; - - if (G_UNLIKELY (buf == NULL)) - goto no_buffer; - - offset += gst_buffer_get_size (buf); - - basesink->segment.position = offset; - - GST_BASE_SINK_PREROLL_LOCK (basesink); - result = gst_base_sink_chain_unlocked (basesink, pad, buf, FALSE); - GST_BASE_SINK_PREROLL_UNLOCK (basesink); - if (G_UNLIKELY (result != GST_FLOW_OK)) - goto paused; - - return; - - /* ERRORS */ -paused: - { - GST_LOG_OBJECT (basesink, "pausing task, reason %s", - gst_flow_get_name (result)); - gst_pad_pause_task (pad); - if (result == GST_FLOW_EOS) { - /* perform EOS logic */ - if (basesink->segment.flags & GST_SEGMENT_FLAG_SEGMENT) { - gst_element_post_message (GST_ELEMENT_CAST (basesink), - gst_message_new_segment_done (GST_OBJECT_CAST (basesink), - basesink->segment.format, basesink->segment.position)); - gst_base_sink_event (pad, parent, - gst_event_new_segment_done (basesink->segment.format, - basesink->segment.position)); - } else { - gst_base_sink_event (pad, parent, gst_event_new_eos ()); - } - } else if (result == GST_FLOW_NOT_LINKED || result <= GST_FLOW_EOS) { - /* for fatal errors we post an error message, post the error - * first so the app knows about the error first. - * wrong-state is not a fatal error because it happens due to - * flushing and posting an error message in that case is the - * wrong thing to do, e.g. when basesrc is doing a flushing - * seek. */ - GST_ELEMENT_FLOW_ERROR (basesink, result); - gst_base_sink_event (pad, parent, gst_event_new_eos ()); - } - return; - } -no_buffer: - { - GST_LOG_OBJECT (basesink, "no buffer, pausing"); - GST_ELEMENT_ERROR (basesink, STREAM, FAILED, - (_("Internal data flow error.")), ("element returned NULL buffer")); - result = GST_FLOW_ERROR; - goto paused; - } -} - -static gboolean -gst_base_sink_set_flushing (GstBaseSink * basesink, GstPad * pad, - gboolean flushing) -{ - GstBaseSinkClass *bclass; - - bclass = GST_BASE_SINK_GET_CLASS (basesink); - - if (flushing) { - /* unlock any subclasses, we need to do this before grabbing the - * PREROLL_LOCK since we hold this lock before going into ::render. */ - if (bclass->unlock) - bclass->unlock (basesink); - } - - GST_BASE_SINK_PREROLL_LOCK (basesink); - basesink->flushing = flushing; - if (flushing) { - /* step 1, now that we have the PREROLL lock, clear our unlock request */ - if (bclass->unlock_stop) - bclass->unlock_stop (basesink); - - /* set need_preroll before we unblock the clock. If the clock is unblocked - * before timing out, we can reuse the buffer for preroll. */ - basesink->need_preroll = TRUE; - - /* step 2, unblock clock sync (if any) or any other blocking thing */ - if (basesink->clock_id) { - gst_clock_id_unschedule (basesink->clock_id); - } - - /* flush out the data thread if it's locked in finish_preroll, this will - * also flush out the EOS state */ - GST_DEBUG_OBJECT (basesink, - "flushing out data thread, need preroll to TRUE"); - - /* we can't have EOS anymore now */ - basesink->eos = FALSE; - basesink->priv->received_eos = FALSE; - basesink->have_preroll = FALSE; - basesink->priv->step_unlock = FALSE; - /* can't report latency anymore until we preroll again */ - if (basesink->priv->async_enabled) { - GST_OBJECT_LOCK (basesink); - basesink->priv->have_latency = FALSE; - GST_OBJECT_UNLOCK (basesink); - } - /* and signal any waiters now */ - GST_BASE_SINK_PREROLL_SIGNAL (basesink); - } - GST_BASE_SINK_PREROLL_UNLOCK (basesink); - - return TRUE; -} - -static gboolean -gst_base_sink_default_activate_pull (GstBaseSink * basesink, gboolean active) -{ - gboolean result; - - if (active) { - /* start task */ - result = gst_pad_start_task (basesink->sinkpad, - (GstTaskFunction) gst_base_sink_loop, basesink->sinkpad, NULL); - } else { - /* step 2, make sure streaming finishes */ - result = gst_pad_stop_task (basesink->sinkpad); - } - - return result; -} - -static gboolean -gst_base_sink_pad_activate (GstPad * pad, GstObject * parent) -{ - gboolean result = FALSE; - GstBaseSink *basesink; - GstQuery *query; - gboolean pull_mode; - - basesink = GST_BASE_SINK (parent); - - GST_DEBUG_OBJECT (basesink, "Trying pull mode first"); - - gst_base_sink_set_flushing (basesink, pad, FALSE); - - /* we need to have the pull mode enabled */ - if (!basesink->can_activate_pull) { - GST_DEBUG_OBJECT (basesink, "pull mode disabled"); - goto fallback; - } - - /* check if downstreams supports pull mode at all */ - query = gst_query_new_scheduling (); - - if (!gst_pad_peer_query (pad, query)) { - gst_query_unref (query); - GST_DEBUG_OBJECT (basesink, "peer query failed, no pull mode"); - goto fallback; - } - - /* parse result of the query */ - pull_mode = gst_query_has_scheduling_mode (query, GST_PAD_MODE_PULL); - gst_query_unref (query); - - if (!pull_mode) { - GST_DEBUG_OBJECT (basesink, "pull mode not supported"); - goto fallback; - } - - /* set the pad mode before starting the task so that it's in the - * correct state for the new thread. also the sink set_caps and get_caps - * function checks this */ - basesink->pad_mode = GST_PAD_MODE_PULL; - - /* we first try to negotiate a format so that when we try to activate - * downstream, it knows about our format */ - if (!gst_base_sink_negotiate_pull (basesink)) { - GST_DEBUG_OBJECT (basesink, "failed to negotiate in pull mode"); - goto fallback; - } - - /* ok activate now */ - if (!gst_pad_activate_mode (pad, GST_PAD_MODE_PULL, TRUE)) { - /* clear any pending caps */ - GST_OBJECT_LOCK (basesink); - gst_caps_replace (&basesink->priv->caps, NULL); - GST_OBJECT_UNLOCK (basesink); - GST_DEBUG_OBJECT (basesink, "failed to activate in pull mode"); - goto fallback; - } - - GST_DEBUG_OBJECT (basesink, "Success activating pull mode"); - result = TRUE; - goto done; - - /* push mode fallback */ -fallback: - GST_DEBUG_OBJECT (basesink, "Falling back to push mode"); - if ((result = gst_pad_activate_mode (pad, GST_PAD_MODE_PUSH, TRUE))) { - GST_DEBUG_OBJECT (basesink, "Success activating push mode"); - } - -done: - if (!result) { - GST_WARNING_OBJECT (basesink, "Could not activate pad in either mode"); - gst_base_sink_set_flushing (basesink, pad, TRUE); - } - - return result; -} - -static gboolean -gst_base_sink_pad_activate_push (GstPad * pad, GstObject * parent, - gboolean active) -{ - gboolean result; - GstBaseSink *basesink; - - basesink = GST_BASE_SINK (parent); - - if (active) { - if (!basesink->can_activate_push) { - result = FALSE; - basesink->pad_mode = GST_PAD_MODE_NONE; - } else { - result = TRUE; - basesink->pad_mode = GST_PAD_MODE_PUSH; - } - } else { - if (G_UNLIKELY (basesink->pad_mode != GST_PAD_MODE_PUSH)) { - g_warning ("Internal GStreamer activation error!!!"); - result = FALSE; - } else { - gst_base_sink_set_flushing (basesink, pad, TRUE); - result = TRUE; - basesink->pad_mode = GST_PAD_MODE_NONE; - } - } - - return result; -} - -static gboolean -gst_base_sink_negotiate_pull (GstBaseSink * basesink) -{ - GstCaps *caps; - gboolean result; - - result = FALSE; - - /* this returns the intersection between our caps and the peer caps. If there - * is no peer, it returns %NULL and we can't operate in pull mode so we can - * fail the negotiation. */ - caps = gst_pad_get_allowed_caps (GST_BASE_SINK_PAD (basesink)); - if (caps == NULL || gst_caps_is_empty (caps)) - goto no_caps_possible; - - GST_DEBUG_OBJECT (basesink, "allowed caps: %" GST_PTR_FORMAT, caps); - - if (gst_caps_is_any (caps)) { - GST_DEBUG_OBJECT (basesink, "caps were ANY after fixating, " - "allowing pull()"); - /* neither side has template caps in this case, so they are prepared for - pull() without setcaps() */ - result = TRUE; - } else { - /* try to fixate */ - caps = gst_base_sink_fixate (basesink, caps); - GST_DEBUG_OBJECT (basesink, "fixated to: %" GST_PTR_FORMAT, caps); - - if (gst_caps_is_fixed (caps)) { - if (!gst_pad_set_caps (GST_BASE_SINK_PAD (basesink), caps)) - goto could_not_set_caps; - - result = TRUE; - } - } - - gst_caps_unref (caps); - - return result; - -no_caps_possible: - { - GST_INFO_OBJECT (basesink, "Pipeline could not agree on caps"); - GST_DEBUG_OBJECT (basesink, "get_allowed_caps() returned EMPTY"); - if (caps) - gst_caps_unref (caps); - return FALSE; - } -could_not_set_caps: - { - GST_INFO_OBJECT (basesink, "Could not set caps: %" GST_PTR_FORMAT, caps); - gst_caps_unref (caps); - return FALSE; - } -} - -/* this won't get called until we implement an activate function */ -static gboolean -gst_base_sink_pad_activate_pull (GstPad * pad, GstObject * parent, - gboolean active) -{ - gboolean result = FALSE; - GstBaseSink *basesink; - GstBaseSinkClass *bclass; - - basesink = GST_BASE_SINK (parent); - bclass = GST_BASE_SINK_GET_CLASS (basesink); - - if (active) { - gint64 duration; - - /* we mark we have a newsegment here because pull based - * mode works just fine without having a newsegment before the - * first buffer */ - gst_segment_init (&basesink->segment, GST_FORMAT_BYTES); - GST_OBJECT_LOCK (basesink); - basesink->have_newsegment = TRUE; - GST_OBJECT_UNLOCK (basesink); - - /* get the peer duration in bytes */ - result = gst_pad_peer_query_duration (pad, GST_FORMAT_BYTES, &duration); - if (result) { - GST_DEBUG_OBJECT (basesink, - "setting duration in bytes to %" G_GINT64_FORMAT, duration); - basesink->segment.duration = duration; - } else { - GST_DEBUG_OBJECT (basesink, "unknown duration"); - } - - if (bclass->activate_pull) - result = bclass->activate_pull (basesink, TRUE); - else - result = FALSE; - - if (!result) - goto activate_failed; - - } else { - if (G_UNLIKELY (basesink->pad_mode != GST_PAD_MODE_PULL)) { - g_warning ("Internal GStreamer activation error!!!"); - result = FALSE; - } else { - result = gst_base_sink_set_flushing (basesink, pad, TRUE); - if (bclass->activate_pull) - result &= bclass->activate_pull (basesink, FALSE); - basesink->pad_mode = GST_PAD_MODE_NONE; - } - } - - return result; - - /* ERRORS */ -activate_failed: - { - /* reset, as starting the thread failed */ - basesink->pad_mode = GST_PAD_MODE_NONE; - - GST_ERROR_OBJECT (basesink, "subclass failed to activate in pull mode"); - return FALSE; - } -} - -static gboolean -gst_base_sink_pad_activate_mode (GstPad * pad, GstObject * parent, - GstPadMode mode, gboolean active) -{ - gboolean res; - - switch (mode) { - case GST_PAD_MODE_PULL: - res = gst_base_sink_pad_activate_pull (pad, parent, active); - break; - case GST_PAD_MODE_PUSH: - res = gst_base_sink_pad_activate_push (pad, parent, active); - break; - default: - GST_LOG_OBJECT (pad, "unknown activation mode %d", mode); - res = FALSE; - break; - } - return res; -} - -/* send an event to our sinkpad peer. */ -static gboolean -gst_base_sink_send_event (GstElement * element, GstEvent * event) -{ - GstPad *pad; - GstBaseSink *basesink = GST_BASE_SINK (element); - gboolean forward, result = TRUE; - GstPadMode mode; - - GST_OBJECT_LOCK (element); - /* get the pad and the scheduling mode */ - pad = gst_object_ref (basesink->sinkpad); - mode = basesink->pad_mode; - GST_OBJECT_UNLOCK (element); - - /* only push UPSTREAM events upstream */ - forward = GST_EVENT_IS_UPSTREAM (event); - - GST_DEBUG_OBJECT (basesink, "handling event %p %" GST_PTR_FORMAT, event, - event); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_LATENCY: - { - GstClockTime latency; - - gst_event_parse_latency (event, &latency); - - /* store the latency. We use this to adjust the running_time before syncing - * it to the clock. */ - GST_OBJECT_LOCK (element); - basesink->priv->latency = latency; - if (!basesink->priv->have_latency) - forward = FALSE; - GST_OBJECT_UNLOCK (element); - GST_DEBUG_OBJECT (basesink, "latency set to %" GST_TIME_FORMAT, - GST_TIME_ARGS (latency)); - - /* We forward this event so that all elements know about the global pipeline - * latency. This is interesting for an element when it wants to figure out - * when a particular piece of data will be rendered. */ - break; - } - case GST_EVENT_INSTANT_RATE_SYNC_TIME: - { - gst_base_sink_perform_instant_rate_change (basesink, pad, event); - - /* Forward the event. If upstream handles it already, it is supposed to - * send a SEGMENT event with the same seqnum and the final rate before - * the next buffer - */ - forward = TRUE; - - break; - } - case GST_EVENT_SEEK: - /* in pull mode we will execute the seek */ - if (mode == GST_PAD_MODE_PULL) - result = gst_base_sink_perform_seek (basesink, pad, event); - break; - case GST_EVENT_STEP: - result = gst_base_sink_perform_step (basesink, pad, event); - forward = FALSE; - break; - default: - break; - } - - if (forward) { - GST_DEBUG_OBJECT (basesink, "sending event %p %" GST_PTR_FORMAT, event, - event); - - /* Compensate for any instant-rate-change related running time offset - * between upstream and the internal running time of the sink */ - if (basesink->priv->instant_rate_sync_seqnum != GST_SEQNUM_INVALID) { - GstClockTime now = GST_CLOCK_TIME_NONE; - GstClockTime actual_duration; - GstClockTime upstream_duration; - GstClockTimeDiff difference; - gboolean is_playing, negative_duration; - - GST_OBJECT_LOCK (basesink); - is_playing = GST_STATE (basesink) == GST_STATE_PLAYING - && (GST_STATE_PENDING (basesink) == GST_STATE_VOID_PENDING || - GST_STATE_PENDING (basesink) == GST_STATE_PLAYING); - - if (is_playing) { - GstClockTime base_time, clock_time; - GstClock *clock; - - base_time = GST_ELEMENT_CAST (basesink)->base_time; - clock = GST_ELEMENT_CLOCK (basesink); - GST_OBJECT_UNLOCK (basesink); - - if (clock) { - clock_time = gst_clock_get_time (clock); - now = clock_time - base_time; - } - } else { - now = GST_ELEMENT_START_TIME (basesink); - GST_OBJECT_UNLOCK (basesink); - } - - GST_DEBUG_OBJECT (basesink, - "Current internal running time %" GST_TIME_FORMAT - ", last internal running time %" GST_TIME_FORMAT, GST_TIME_ARGS (now), - GST_TIME_ARGS (basesink->priv->last_anchor_running_time)); - - if (now != GST_CLOCK_TIME_NONE) { - /* Calculate how much running time was spent since the last switch/segment - * in the "corrected upstream segment", our segment */ - /* Due to rounding errors and other inaccuracies, it can happen - * that our calculated internal running time is before the upstream - * running time. We need to compensate for that */ - if (now < basesink->priv->last_anchor_running_time) { - actual_duration = basesink->priv->last_anchor_running_time - now; - negative_duration = TRUE; - } else { - actual_duration = now - basesink->priv->last_anchor_running_time; - negative_duration = FALSE; - } - - /* Transpose that duration (i.e. what upstream beliefs) */ - upstream_duration = - (actual_duration * basesink->segment.rate) / - basesink->priv->upstream_segment.rate; - - /* Add the difference to the previously accumulated correction */ - if (negative_duration) - difference = upstream_duration - actual_duration; - else - difference = actual_duration - upstream_duration; - - GST_DEBUG_OBJECT (basesink, - "Current instant rate correction offset. Actual duration %" - GST_TIME_FORMAT ", upstream duration %" GST_TIME_FORMAT - ", negative %d, difference %" GST_STIME_FORMAT ", current offset %" - GST_STIME_FORMAT, GST_TIME_ARGS (actual_duration), - GST_TIME_ARGS (upstream_duration), negative_duration, - GST_STIME_ARGS (difference), - GST_STIME_ARGS (basesink->priv->instant_rate_offset + difference)); - - difference = basesink->priv->instant_rate_offset + difference; - - event = gst_event_make_writable (event); - gst_event_set_running_time_offset (event, -difference); - } - } - - result = gst_pad_push_event (pad, event); - } else { - /* not forwarded, unref the event */ - gst_event_unref (event); - } - - gst_object_unref (pad); - - GST_DEBUG_OBJECT (basesink, "handled event: %d", result); - - return result; -} - -static gboolean -gst_base_sink_get_position (GstBaseSink * basesink, GstFormat format, - gint64 * cur, gboolean * upstream) -{ - GstClock *clock = NULL; - gboolean res = FALSE; - GstFormat oformat; - GstSegment *segment; - GstClockTime now, latency; - GstClockTimeDiff base_time; - gint64 time, base, offset, duration; - gdouble rate; - gint64 last; - gboolean last_seen, with_clock, in_paused; - - GST_OBJECT_LOCK (basesink); - /* we can only get the segment when we are not NULL or READY */ - if (!basesink->have_newsegment) - goto wrong_state; - - in_paused = FALSE; - /* when not in PLAYING or when we're busy with a state change, we - * cannot read from the clock so we report time based on the - * last seen timestamp. */ - if (GST_STATE (basesink) != GST_STATE_PLAYING || - GST_STATE_PENDING (basesink) != GST_STATE_VOID_PENDING) { - in_paused = TRUE; - } - - segment = &basesink->segment; - - /* get the format in the segment */ - oformat = segment->format; - - /* report with last seen position when EOS */ - last_seen = basesink->eos; - - /* assume we will use the clock for getting the current position */ - with_clock = TRUE; - if (!basesink->sync) - with_clock = FALSE; - - /* and we need a clock */ - if (G_UNLIKELY ((clock = GST_ELEMENT_CLOCK (basesink)) == NULL)) - with_clock = FALSE; - else - gst_object_ref (clock); - - /* mainloop might be querying position when going to playing async, - * while (audio) rendering might be quickly advancing stream position, - * so use clock asap rather than last reported position */ - if (in_paused && with_clock && g_atomic_int_get (&basesink->priv->to_playing)) { - GST_DEBUG_OBJECT (basesink, "going to PLAYING, so not PAUSED"); - in_paused = FALSE; - } - - /* collect all data we need holding the lock */ - if (GST_CLOCK_TIME_IS_VALID (segment->time)) - time = segment->time; - else - time = 0; - - if (GST_CLOCK_TIME_IS_VALID (segment->offset)) - offset = segment->offset; - else - offset = 0; - - if (GST_CLOCK_TIME_IS_VALID (segment->stop)) - duration = segment->stop - segment->start; - else - duration = 0; - - base = segment->base; - rate = segment->rate * segment->applied_rate; - latency = basesink->priv->latency; - - if (in_paused) { - /* in paused, use start_time */ - base_time = GST_ELEMENT_START_TIME (basesink); - GST_DEBUG_OBJECT (basesink, "in paused, using start time %" GST_TIME_FORMAT, - GST_TIME_ARGS (base_time)); - } else if (with_clock) { - /* else use clock when needed */ - base_time = GST_ELEMENT_CAST (basesink)->base_time; - GST_DEBUG_OBJECT (basesink, "using clock and base time %" GST_TIME_FORMAT, - GST_TIME_ARGS (base_time)); - } else { - /* else, no sync or clock -> no base time */ - GST_DEBUG_OBJECT (basesink, "no sync or no clock"); - base_time = -1; - } - - /* no base_time, we can't calculate running_time, use last seem timestamp to report - * time */ - if (base_time == -1) - last_seen = TRUE; - - if (oformat == GST_FORMAT_TIME) { - gint64 start, stop; - - start = basesink->priv->current_sstart; - stop = basesink->priv->current_sstop; - - if (last_seen) { - /* when we don't use the clock, we use the last position as a lower bound */ - if (stop == -1 || segment->rate > 0.0) - last = start; - else - last = stop; - - GST_DEBUG_OBJECT (basesink, "in PAUSED using last %" GST_TIME_FORMAT, - GST_TIME_ARGS (last)); - } else { - /* in playing and paused, use last stop time as upper bound */ - if (start == -1 || segment->rate > 0.0) - last = stop; - else - last = start; - - GST_DEBUG_OBJECT (basesink, "in PLAYING using last %" GST_TIME_FORMAT, - GST_TIME_ARGS (last)); - } - } else { - /* convert position to stream time */ - last = gst_segment_to_stream_time (segment, oformat, segment->position); - - GST_DEBUG_OBJECT (basesink, "in using last %" G_GINT64_FORMAT, last); - } - - /* need to release the object lock before we can get the time, - * a clock might take the LOCK of the provider, which could be - * a basesink subclass. */ - GST_OBJECT_UNLOCK (basesink); - - if (last_seen) { - /* in EOS or when no valid stream_time, report the value of last seen - * timestamp */ - if (last == -1) { - /* no timestamp, we need to ask upstream */ - GST_DEBUG_OBJECT (basesink, "no last seen timestamp, asking upstream"); - res = FALSE; - *upstream = TRUE; - goto done; - } - GST_DEBUG_OBJECT (basesink, "using last seen timestamp %" GST_TIME_FORMAT, - GST_TIME_ARGS (last)); - *cur = last; - } else { - if (oformat != GST_FORMAT_TIME) { - /* convert base, time and duration to time */ - if (!gst_pad_query_convert (basesink->sinkpad, oformat, base, - GST_FORMAT_TIME, &base)) - goto convert_failed; - if (!gst_pad_query_convert (basesink->sinkpad, oformat, duration, - GST_FORMAT_TIME, &duration)) - goto convert_failed; - if (!gst_pad_query_convert (basesink->sinkpad, oformat, time, - GST_FORMAT_TIME, &time)) - goto convert_failed; - if (!gst_pad_query_convert (basesink->sinkpad, oformat, last, - GST_FORMAT_TIME, &last)) - goto convert_failed; - - /* assume time format from now on */ - oformat = GST_FORMAT_TIME; - } - - if (!in_paused && with_clock) { - now = gst_clock_get_time (clock); - } else { - now = base_time; - base_time = 0; - } - - /* subtract base time and base time from the clock time. - * Make sure we don't go negative. This is the current time in - * the segment which we need to scale with the combined - * rate and applied rate. */ - base_time += base; - base_time += latency; - if (GST_CLOCK_DIFF (base_time, now) < 0) - base_time = now; - - /* for negative rates we need to count back from the segment - * duration. */ - if (rate < 0.0) - time += duration; - - *cur = time + offset + gst_guint64_to_gdouble (now - base_time) * rate; - - /* never report more than last seen position */ - if (last != -1) { - if (rate > 0.0) - *cur = MIN (last, *cur); - else - *cur = MAX (last, *cur); - } - - GST_DEBUG_OBJECT (basesink, - "now %" GST_TIME_FORMAT " - base_time %" GST_TIME_FORMAT " - base %" - GST_TIME_FORMAT " + time %" GST_TIME_FORMAT " last %" GST_TIME_FORMAT, - GST_TIME_ARGS (now), GST_TIME_ARGS (base_time), GST_TIME_ARGS (base), - GST_TIME_ARGS (time), GST_TIME_ARGS (last)); - } - - if (oformat != format) { - /* convert to final format */ - if (!gst_pad_query_convert (basesink->sinkpad, oformat, *cur, format, cur)) - goto convert_failed; - } - - res = TRUE; - -done: - GST_DEBUG_OBJECT (basesink, "res: %d, POSITION: %" GST_TIME_FORMAT, - res, GST_TIME_ARGS (*cur)); - - if (clock) - gst_object_unref (clock); - - return res; - - /* special cases */ -wrong_state: - { - /* in NULL or READY we always return FALSE and -1 */ - GST_DEBUG_OBJECT (basesink, "position in wrong state, return -1"); - res = FALSE; - *cur = -1; - GST_OBJECT_UNLOCK (basesink); - goto done; - } -convert_failed: - { - GST_DEBUG_OBJECT (basesink, "convert failed, try upstream"); - *upstream = TRUE; - res = FALSE; - goto done; - } -} - -static gboolean -gst_base_sink_get_duration (GstBaseSink * basesink, GstFormat format, - gint64 * dur, gboolean * upstream) -{ - gboolean res = FALSE; - - if (basesink->pad_mode == GST_PAD_MODE_PULL) { - gint64 uduration; - - /* get the duration in bytes, in pull mode that's all we are sure to - * know. We have to explicitly get this value from upstream instead of - * using our cached value because it might change. Duration caching - * should be done at a higher level. */ - res = - gst_pad_peer_query_duration (basesink->sinkpad, GST_FORMAT_BYTES, - &uduration); - if (res) { - basesink->segment.duration = uduration; - if (format != GST_FORMAT_BYTES) { - /* convert to the requested format */ - res = - gst_pad_query_convert (basesink->sinkpad, GST_FORMAT_BYTES, - uduration, format, dur); - } else { - *dur = uduration; - } - } - *upstream = FALSE; - } else { - *upstream = TRUE; - } - - return res; -} - -static gboolean -default_element_query (GstElement * element, GstQuery * query) -{ - gboolean res = FALSE; - - GstBaseSink *basesink = GST_BASE_SINK (element); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - { - gint64 cur = 0; - GstFormat format; - gboolean upstream = FALSE; - - gst_query_parse_position (query, &format, NULL); - - GST_DEBUG_OBJECT (basesink, "position query in format %s", - gst_format_get_name (format)); - - /* first try to get the position based on the clock */ - if ((res = - gst_base_sink_get_position (basesink, format, &cur, &upstream))) { - gst_query_set_position (query, format, cur); - } else if (upstream) { - /* fallback to peer query */ - res = gst_pad_peer_query (basesink->sinkpad, query); - } - if (!res) { - /* we can handle a few things if upstream failed */ - if (format == GST_FORMAT_PERCENT) { - gint64 dur = 0; - - res = gst_base_sink_get_position (basesink, GST_FORMAT_TIME, &cur, - &upstream); - if (!res && upstream) { - res = - gst_pad_peer_query_position (basesink->sinkpad, GST_FORMAT_TIME, - &cur); - } - if (res) { - res = gst_base_sink_get_duration (basesink, GST_FORMAT_TIME, &dur, - &upstream); - if (!res && upstream) { - res = - gst_pad_peer_query_duration (basesink->sinkpad, - GST_FORMAT_TIME, &dur); - } - } - if (res) { - gint64 pos; - - pos = gst_util_uint64_scale (100 * GST_FORMAT_PERCENT_SCALE, cur, - dur); - gst_query_set_position (query, GST_FORMAT_PERCENT, pos); - } - } - } - break; - } - case GST_QUERY_DURATION: - { - gint64 dur = 0; - GstFormat format; - gboolean upstream = FALSE; - - gst_query_parse_duration (query, &format, NULL); - - GST_DEBUG_OBJECT (basesink, "duration query in format %s", - gst_format_get_name (format)); - - if ((res = - gst_base_sink_get_duration (basesink, format, &dur, &upstream))) { - gst_query_set_duration (query, format, dur); - } else if (upstream) { - /* fallback to peer query */ - res = gst_pad_peer_query (basesink->sinkpad, query); - } - if (!res) { - /* we can handle a few things if upstream failed */ - if (format == GST_FORMAT_PERCENT) { - gst_query_set_duration (query, GST_FORMAT_PERCENT, - GST_FORMAT_PERCENT_MAX); - res = TRUE; - } - } - break; - } - case GST_QUERY_LATENCY: - { - gboolean live, us_live; - GstClockTime min, max; - - if ((res = gst_base_sink_query_latency (basesink, &live, &us_live, &min, - &max))) { - gst_query_set_latency (query, live, min, max); - } - break; - } - case GST_QUERY_JITTER: - break; - case GST_QUERY_RATE: - /* gst_query_set_rate (query, basesink->segment_rate); */ - res = TRUE; - break; - case GST_QUERY_SEGMENT: - { - if (basesink->pad_mode == GST_PAD_MODE_PULL) { - GstFormat format; - gint64 start, stop; - - format = basesink->segment.format; - - start = - gst_segment_to_stream_time (&basesink->segment, format, - basesink->segment.start); - if ((stop = basesink->segment.stop) == -1) - stop = basesink->segment.duration; - else - stop = gst_segment_to_stream_time (&basesink->segment, format, stop); - - gst_query_set_segment (query, basesink->segment.rate, format, start, - stop); - res = TRUE; - } else { - res = gst_pad_peer_query (basesink->sinkpad, query); - } - break; - } - case GST_QUERY_SEEKING: - case GST_QUERY_CONVERT: - case GST_QUERY_FORMATS: - default: - res = gst_pad_peer_query (basesink->sinkpad, query); - break; - } - GST_DEBUG_OBJECT (basesink, "query %s returns %d", - GST_QUERY_TYPE_NAME (query), res); - return res; -} - -static void -gst_base_sink_drain (GstBaseSink * basesink) -{ - GstBuffer *old; - GstBufferList *old_list; - - GST_OBJECT_LOCK (basesink); - if ((old = basesink->priv->last_buffer)) - basesink->priv->last_buffer = gst_buffer_copy_deep (old); - - if ((old_list = basesink->priv->last_buffer_list)) - basesink->priv->last_buffer_list = gst_buffer_list_copy_deep (old_list); - GST_OBJECT_UNLOCK (basesink); - - if (old) - gst_buffer_unref (old); - if (old_list) - gst_mini_object_unref (GST_MINI_OBJECT_CAST (old_list)); -} - -static gboolean -gst_base_sink_default_query (GstBaseSink * basesink, GstQuery * query) -{ - gboolean res; - GstBaseSinkClass *bclass; - - bclass = GST_BASE_SINK_GET_CLASS (basesink); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_ALLOCATION: - { - gst_base_sink_drain (basesink); - if (bclass->propose_allocation) - res = bclass->propose_allocation (basesink, query); - else - res = FALSE; - break; - } - case GST_QUERY_CAPS: - { - GstCaps *caps, *filter; - - gst_query_parse_caps (query, &filter); - caps = gst_base_sink_query_caps (basesink, basesink->sinkpad, filter); - gst_query_set_caps_result (query, caps); - gst_caps_unref (caps); - res = TRUE; - break; - } - case GST_QUERY_ACCEPT_CAPS: - { - GstCaps *caps, *allowed; - gboolean subset; - - /* slightly faster than the default implementation */ - gst_query_parse_accept_caps (query, &caps); - allowed = gst_base_sink_query_caps (basesink, basesink->sinkpad, NULL); - subset = gst_caps_is_subset (caps, allowed); - GST_DEBUG_OBJECT (basesink, "Checking if requested caps %" GST_PTR_FORMAT - " are a subset of pad caps %" GST_PTR_FORMAT " result %d", caps, - allowed, subset); - gst_caps_unref (allowed); - gst_query_set_accept_caps_result (query, subset); - res = TRUE; - break; - } - case GST_QUERY_DRAIN: - { - gst_base_sink_drain (basesink); - res = TRUE; - break; - } - case GST_QUERY_POSITION: - { - res = default_element_query (GST_ELEMENT (basesink), query); - break; - } - default: - res = - gst_pad_query_default (basesink->sinkpad, GST_OBJECT_CAST (basesink), - query); - break; - } - return res; -} - -static gboolean -gst_base_sink_sink_query (GstPad * pad, GstObject * parent, GstQuery * query) -{ - GstBaseSink *basesink; - GstBaseSinkClass *bclass; - gboolean res; - - basesink = GST_BASE_SINK_CAST (parent); - bclass = GST_BASE_SINK_GET_CLASS (basesink); - - if (bclass->query) - res = bclass->query (basesink, query); - else - res = FALSE; - - return res; -} - -static GstStateChangeReturn -gst_base_sink_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; - GstBaseSink *basesink = GST_BASE_SINK (element); - GstBaseSinkClass *bclass; - GstBaseSinkPrivate *priv; - - priv = basesink->priv; - - bclass = GST_BASE_SINK_GET_CLASS (basesink); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - if (bclass->start) - if (!bclass->start (basesink)) - goto start_failed; - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - /* need to complete preroll before this state change completes, there - * is no data flow in READY so we can safely assume we need to preroll. */ - GST_BASE_SINK_PREROLL_LOCK (basesink); - GST_DEBUG_OBJECT (basesink, "READY to PAUSED"); - basesink->have_newsegment = FALSE; - gst_segment_init (&basesink->segment, GST_FORMAT_UNDEFINED); - gst_segment_init (&basesink->priv->upstream_segment, - GST_FORMAT_UNDEFINED); - basesink->offset = 0; - basesink->have_preroll = FALSE; - priv->step_unlock = FALSE; - basesink->need_preroll = TRUE; - basesink->playing_async = TRUE; - priv->current_sstart = GST_CLOCK_TIME_NONE; - priv->current_sstop = GST_CLOCK_TIME_NONE; - priv->eos_rtime = GST_CLOCK_TIME_NONE; - priv->latency = 0; - basesink->eos = FALSE; - priv->received_eos = FALSE; - gst_base_sink_reset_qos (basesink); - priv->rc_next = -1; - priv->committed = FALSE; - priv->call_preroll = TRUE; - priv->current_step.valid = FALSE; - priv->pending_step.valid = FALSE; - priv->instant_rate_sync_seqnum = GST_SEQNUM_INVALID; - priv->instant_rate_multiplier = 0; - priv->last_instant_rate_seqnum = GST_SEQNUM_INVALID; - priv->segment_seqnum = GST_SEQNUM_INVALID; - priv->instant_rate_offset = 0; - priv->last_anchor_running_time = 0; - if (priv->async_enabled) { - GST_DEBUG_OBJECT (basesink, "doing async state change"); - /* when async enabled, post async-start message and return ASYNC from - * the state change function */ - ret = GST_STATE_CHANGE_ASYNC; - gst_element_post_message (GST_ELEMENT_CAST (basesink), - gst_message_new_async_start (GST_OBJECT_CAST (basesink))); - } else { - priv->have_latency = TRUE; - gst_element_post_message (GST_ELEMENT_CAST (basesink), - gst_message_new_latency (GST_OBJECT_CAST (basesink))); - } - GST_BASE_SINK_PREROLL_UNLOCK (basesink); - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - GST_BASE_SINK_PREROLL_LOCK (basesink); - g_atomic_int_set (&basesink->priv->to_playing, TRUE); - if (!gst_base_sink_needs_preroll (basesink)) { - GST_DEBUG_OBJECT (basesink, "PAUSED to PLAYING, don't need preroll"); - /* no preroll needed anymore now. */ - basesink->playing_async = FALSE; - basesink->need_preroll = FALSE; - if (basesink->eos) { - GstMessage *message; - - /* need to post EOS message here */ - GST_DEBUG_OBJECT (basesink, "Now posting EOS"); - message = gst_message_new_eos (GST_OBJECT_CAST (basesink)); - gst_message_set_seqnum (message, basesink->priv->seqnum); - gst_element_post_message (GST_ELEMENT_CAST (basesink), message); - } else { - GST_DEBUG_OBJECT (basesink, "signal preroll"); - GST_BASE_SINK_PREROLL_SIGNAL (basesink); - } - } else { - GST_DEBUG_OBJECT (basesink, "PAUSED to PLAYING, we are not prerolled"); - basesink->need_preroll = TRUE; - basesink->playing_async = TRUE; - priv->call_preroll = TRUE; - priv->committed = FALSE; - if (priv->async_enabled) { - GST_DEBUG_OBJECT (basesink, "doing async state change"); - ret = GST_STATE_CHANGE_ASYNC; - gst_element_post_message (GST_ELEMENT_CAST (basesink), - gst_message_new_async_start (GST_OBJECT_CAST (basesink))); - } - } - GST_BASE_SINK_PREROLL_UNLOCK (basesink); - break; - default: - break; - } - - { - GstStateChangeReturn bret; - - bret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - if (G_UNLIKELY (bret == GST_STATE_CHANGE_FAILURE)) - goto activate_failed; - } - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - /* completed transition, so need not be marked any longer - * And it should be unmarked, since e.g. losing our position upon flush - * does not really change state to PAUSED ... */ - g_atomic_int_set (&basesink->priv->to_playing, FALSE); - break; - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - g_atomic_int_set (&basesink->priv->to_playing, FALSE); - GST_DEBUG_OBJECT (basesink, "PLAYING to PAUSED"); - /* FIXME, make sure we cannot enter _render first */ - - /* we need to call ::unlock before locking PREROLL_LOCK - * since we lock it before going into ::render */ - if (bclass->unlock) - bclass->unlock (basesink); - - GST_BASE_SINK_PREROLL_LOCK (basesink); - GST_DEBUG_OBJECT (basesink, "got preroll lock"); - /* now that we have the PREROLL lock, clear our unlock request */ - if (bclass->unlock_stop) - bclass->unlock_stop (basesink); - - if (basesink->clock_id) { - GST_DEBUG_OBJECT (basesink, "unschedule clock"); - gst_clock_id_unschedule (basesink->clock_id); - } - - /* if we don't have a preroll buffer we need to wait for a preroll and - * return ASYNC. */ - if (!gst_base_sink_needs_preroll (basesink)) { - GST_DEBUG_OBJECT (basesink, "PLAYING to PAUSED, we are prerolled"); - basesink->playing_async = FALSE; - basesink->need_preroll = FALSE; - } else { - if (GST_STATE_TARGET (GST_ELEMENT (basesink)) <= GST_STATE_READY) { - GST_DEBUG_OBJECT (basesink, "element is <= READY"); - ret = GST_STATE_CHANGE_SUCCESS; - } else { - GST_DEBUG_OBJECT (basesink, - "PLAYING to PAUSED, we are not prerolled"); - basesink->playing_async = TRUE; - basesink->need_preroll = TRUE; - priv->committed = FALSE; - priv->call_preroll = TRUE; - if (priv->async_enabled) { - GST_DEBUG_OBJECT (basesink, "doing async state change"); - ret = GST_STATE_CHANGE_ASYNC; - gst_element_post_message (GST_ELEMENT_CAST (basesink), - gst_message_new_async_start (GST_OBJECT_CAST (basesink))); - } - } - } - GST_DEBUG_OBJECT (basesink, "rendered: %" G_GUINT64_FORMAT - ", dropped: %" G_GUINT64_FORMAT, priv->rendered, priv->dropped); - - gst_base_sink_reset_qos (basesink); - GST_BASE_SINK_PREROLL_UNLOCK (basesink); - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - GST_BASE_SINK_PREROLL_LOCK (basesink); - /* start by resetting our position state with the object lock so that the - * position query gets the right idea. We do this before we post the - * messages so that the message handlers pick this up. */ - GST_OBJECT_LOCK (basesink); - basesink->have_newsegment = FALSE; - priv->current_sstart = GST_CLOCK_TIME_NONE; - priv->current_sstop = GST_CLOCK_TIME_NONE; - priv->have_latency = FALSE; - if (priv->cached_clock_id) { - gst_clock_id_unref (priv->cached_clock_id); - priv->cached_clock_id = NULL; - } - gst_caps_replace (&basesink->priv->caps, NULL); - GST_OBJECT_UNLOCK (basesink); - - gst_base_sink_set_last_buffer (basesink, NULL); - gst_base_sink_set_last_buffer_list (basesink, NULL); - priv->call_preroll = FALSE; - - if (!priv->committed) { - if (priv->async_enabled) { - GST_DEBUG_OBJECT (basesink, "PAUSED to READY, posting async-done"); - - gst_element_post_message (GST_ELEMENT_CAST (basesink), - gst_message_new_state_changed (GST_OBJECT_CAST (basesink), - GST_STATE_PLAYING, GST_STATE_PAUSED, GST_STATE_READY)); - - gst_element_post_message (GST_ELEMENT_CAST (basesink), - gst_message_new_async_done (GST_OBJECT_CAST (basesink), - GST_CLOCK_TIME_NONE)); - } - priv->committed = TRUE; - } else { - GST_DEBUG_OBJECT (basesink, "PAUSED to READY, don't need_preroll"); - } - GST_BASE_SINK_PREROLL_UNLOCK (basesink); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - if (bclass->stop) { - if (!bclass->stop (basesink)) { - GST_WARNING_OBJECT (basesink, "failed to stop"); - } - } - gst_base_sink_set_last_buffer (basesink, NULL); - gst_base_sink_set_last_buffer_list (basesink, NULL); - priv->call_preroll = FALSE; - break; - default: - break; - } - - return ret; - - /* ERRORS */ -start_failed: - { - GST_DEBUG_OBJECT (basesink, "failed to start"); - /* subclass is supposed to post a message but we post one as a fallback - * just in case */ - GST_ELEMENT_ERROR (basesink, CORE, STATE_CHANGE, (NULL), - ("Failed to start")); - return GST_STATE_CHANGE_FAILURE; - } -activate_failed: - { - GST_DEBUG_OBJECT (basesink, - "element failed to change states -- activation problem?"); - return GST_STATE_CHANGE_FAILURE; - } -} - -/** - * gst_base_sink_get_stats: - * @sink: #GstBaseSink - * - * Return various #GstBaseSink statistics. This function returns a #GstStructure - * with name `application/x-gst-base-sink-stats` with the following fields: - * - * - "average-rate" G_TYPE_DOUBLE average frame rate - * - "dropped" G_TYPE_UINT64 Number of dropped frames - * - "rendered" G_TYPE_UINT64 Number of rendered frames - * - * Returns: (transfer full): pointer to #GstStructure - * - * Since: 1.18 - */ -GstStructure * -gst_base_sink_get_stats (GstBaseSink * sink) -{ - GstBaseSinkPrivate *priv = NULL; - - g_return_val_if_fail (sink != NULL, NULL); - priv = sink->priv; - return gst_structure_new ("application/x-gst-base-sink-stats", - "average-rate", G_TYPE_DOUBLE, priv->avg_rate, - "dropped", G_TYPE_UINT64, priv->dropped, - "rendered", G_TYPE_UINT64, priv->rendered, NULL); -} diff --git a/libs/gst/base/gstbasesink.h b/libs/gst/base/gstbasesink.h deleted file mode 100644 index 3745fa20ab..0000000000 --- a/libs/gst/base/gstbasesink.h +++ /dev/null @@ -1,339 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> - * 2000 Wim Taymans <wtay@chello.be> - * - * gstbasesink.h: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_BASE_SINK_H__ -#define __GST_BASE_SINK_H__ - -#include <gst/gst.h> -#include <gst/base/base-prelude.h> - -G_BEGIN_DECLS - - -#define GST_TYPE_BASE_SINK (gst_base_sink_get_type()) -#define GST_BASE_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASE_SINK,GstBaseSink)) -#define GST_BASE_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASE_SINK,GstBaseSinkClass)) -#define GST_BASE_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_BASE_SINK, GstBaseSinkClass)) -#define GST_IS_BASE_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASE_SINK)) -#define GST_IS_BASE_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASE_SINK)) -#define GST_BASE_SINK_CAST(obj) ((GstBaseSink *) (obj)) - -/** - * GST_BASE_SINK_PAD: - * @obj: base sink instance - * - * Gives the pointer to the #GstPad object of the element. - */ -#define GST_BASE_SINK_PAD(obj) (GST_BASE_SINK_CAST (obj)->sinkpad) - -#define GST_BASE_SINK_GET_PREROLL_LOCK(obj) (&GST_BASE_SINK_CAST(obj)->preroll_lock) -#define GST_BASE_SINK_PREROLL_LOCK(obj) (g_mutex_lock(GST_BASE_SINK_GET_PREROLL_LOCK(obj))) -#define GST_BASE_SINK_PREROLL_TRYLOCK(obj) (g_mutex_trylock(GST_BASE_SINK_GET_PREROLL_LOCK(obj))) -#define GST_BASE_SINK_PREROLL_UNLOCK(obj) (g_mutex_unlock(GST_BASE_SINK_GET_PREROLL_LOCK(obj))) - -#define GST_BASE_SINK_GET_PREROLL_COND(obj) (&GST_BASE_SINK_CAST(obj)->preroll_cond) -#define GST_BASE_SINK_PREROLL_WAIT(obj) \ - g_cond_wait (GST_BASE_SINK_GET_PREROLL_COND (obj), GST_BASE_SINK_GET_PREROLL_LOCK (obj)) -#define GST_BASE_SINK_PREROLL_WAIT_UNTIL(obj, end_time) \ - g_cond_wait_until (GST_BASE_SINK_GET_PREROLL_COND (obj), GST_BASE_SINK_GET_PREROLL_LOCK (obj), end_time) -#define GST_BASE_SINK_PREROLL_SIGNAL(obj) g_cond_signal (GST_BASE_SINK_GET_PREROLL_COND (obj)); -#define GST_BASE_SINK_PREROLL_BROADCAST(obj) g_cond_broadcast (GST_BASE_SINK_GET_PREROLL_COND (obj)); - -typedef struct _GstBaseSink GstBaseSink; -typedef struct _GstBaseSinkClass GstBaseSinkClass; -typedef struct _GstBaseSinkPrivate GstBaseSinkPrivate; - -/** - * GstBaseSink: - * - * The opaque #GstBaseSink data structure. - */ -struct _GstBaseSink { - GstElement element; - - /*< protected >*/ - GstPad *sinkpad; - GstPadMode pad_mode; - - /*< protected >*/ /* with LOCK */ - guint64 offset; - gboolean can_activate_pull; - gboolean can_activate_push; - - /*< protected >*/ /* with PREROLL_LOCK */ - GMutex preroll_lock; - GCond preroll_cond; - gboolean eos; - gboolean need_preroll; - gboolean have_preroll; - gboolean playing_async; - - /*< protected >*/ /* with STREAM_LOCK */ - gboolean have_newsegment; - GstSegment segment; - - /*< private >*/ /* with LOCK */ - GstClockID clock_id; - gboolean sync; - gboolean flushing; - gboolean running; - - gint64 max_lateness; - - /*< private >*/ - GstBaseSinkPrivate *priv; - - gpointer _gst_reserved[GST_PADDING_LARGE]; -}; - -/** - * GstBaseSinkClass: - * @parent_class: Element parent class - * @get_caps: Called to get sink pad caps from the subclass - * @set_caps: Notify subclass of changed caps - * @fixate: Only useful in pull mode. Implement if you have - * ideas about what should be the default values for the caps you support. - * @activate_pull: Subclasses should override this when they can provide an - * alternate method of spawning a thread to drive the pipeline in pull mode. - * Should start or stop the pulling thread, depending on the value of the - * "active" argument. Called after actually activating the sink pad in pull - * mode. The default implementation starts a task on the sink pad. - * @get_times: Called to get the start and end times for synchronising - * the passed buffer to the clock - * @propose_allocation: configure the allocation query - * @start: Start processing. Ideal for opening resources in the subclass - * @stop: Stop processing. Subclasses should use this to close resources. - * @unlock: Unlock any pending access to the resource. Subclasses should - * unblock any blocked function ASAP and call gst_base_sink_wait_preroll() - * @unlock_stop: Clear the previous unlock request. Subclasses should clear - * any state they set during #GstBaseSinkClass::unlock, and be ready to - * continue where they left off after gst_base_sink_wait_preroll(), - * gst_base_sink_wait() or gst_wait_sink_wait_clock() return or - * #GstBaseSinkClass::render is called again. - * @query: perform a #GstQuery on the element. - * @event: Override this to handle events arriving on the sink pad - * @wait_event: Override this to implement custom logic to wait for the event - * time (for events like EOS and GAP). Subclasses should always first - * chain up to the default implementation. - * @prepare: Called to prepare the buffer for @render and @preroll. This - * function is called before synchronisation is performed. - * @prepare_list: Called to prepare the buffer list for @render_list. This - * function is called before synchronisation is performed. - * @preroll: Called to present the preroll buffer if desired. - * @render: Called when a buffer should be presented or output, at the - * correct moment if the #GstBaseSink has been set to sync to the clock. - * @render_list: Same as @render but used with buffer lists instead of - * buffers. - * - * Subclasses can override any of the available virtual methods or not, as - * needed. At the minimum, the @render method should be overridden to - * output/present buffers. - */ -struct _GstBaseSinkClass { - GstElementClass parent_class; - - /** - * GstBaseSink::get_caps: - * @filter: (in) (nullable): - * - * Called to get sink pad caps from the subclass. - */ - GstCaps* (*get_caps) (GstBaseSink *sink, GstCaps *filter); - /* notify subclass of new caps */ - gboolean (*set_caps) (GstBaseSink *sink, GstCaps *caps); - - /* fixate sink caps during pull-mode negotiation */ - GstCaps * (*fixate) (GstBaseSink *sink, GstCaps *caps); - /* start or stop a pulling thread */ - gboolean (*activate_pull)(GstBaseSink *sink, gboolean active); - - /* get the start and end times for syncing on this buffer */ - void (*get_times) (GstBaseSink *sink, GstBuffer *buffer, - GstClockTime *start, GstClockTime *end); - - /* propose allocation parameters for upstream */ - gboolean (*propose_allocation) (GstBaseSink *sink, GstQuery *query); - - /* start and stop processing, ideal for opening/closing the resource */ - gboolean (*start) (GstBaseSink *sink); - gboolean (*stop) (GstBaseSink *sink); - - /* unlock any pending access to the resource. subclasses should unlock - * any function ASAP. */ - gboolean (*unlock) (GstBaseSink *sink); - /* Clear a previously indicated unlock request not that unlocking is - * complete. Sub-classes should clear any command queue or indicator they - * set during unlock */ - gboolean (*unlock_stop) (GstBaseSink *sink); - - /* notify subclass of query */ - gboolean (*query) (GstBaseSink *sink, GstQuery *query); - - /* notify subclass of event */ - gboolean (*event) (GstBaseSink *sink, GstEvent *event); - /* wait for eos or gap, subclasses should chain up to parent first */ - GstFlowReturn (*wait_event) (GstBaseSink *sink, GstEvent *event); - - /* notify subclass of buffer or list before doing sync */ - GstFlowReturn (*prepare) (GstBaseSink *sink, GstBuffer *buffer); - GstFlowReturn (*prepare_list) (GstBaseSink *sink, GstBufferList *buffer_list); - - /* notify subclass of preroll buffer or real buffer */ - GstFlowReturn (*preroll) (GstBaseSink *sink, GstBuffer *buffer); - GstFlowReturn (*render) (GstBaseSink *sink, GstBuffer *buffer); - /* Render a BufferList */ - GstFlowReturn (*render_list) (GstBaseSink *sink, GstBufferList *buffer_list); - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING_LARGE]; -}; - -GST_BASE_API -GType gst_base_sink_get_type (void); - -GST_BASE_API -GstFlowReturn gst_base_sink_do_preroll (GstBaseSink *sink, GstMiniObject *obj); - -GST_BASE_API -GstFlowReturn gst_base_sink_wait_preroll (GstBaseSink *sink); - -/* synchronizing against the clock */ - -GST_BASE_API -void gst_base_sink_set_sync (GstBaseSink *sink, gboolean sync); - -GST_BASE_API -gboolean gst_base_sink_get_sync (GstBaseSink *sink); - -/* Drop buffers which are out of segment */ - -GST_BASE_API -void gst_base_sink_set_drop_out_of_segment (GstBaseSink *sink, gboolean drop_out_of_segment); - -GST_BASE_API -gboolean gst_base_sink_get_drop_out_of_segment (GstBaseSink *sink); - -/* dropping late buffers */ - -GST_BASE_API -void gst_base_sink_set_max_lateness (GstBaseSink *sink, gint64 max_lateness); - -GST_BASE_API -gint64 gst_base_sink_get_max_lateness (GstBaseSink *sink); - -/* performing QoS */ - -GST_BASE_API -void gst_base_sink_set_qos_enabled (GstBaseSink *sink, gboolean enabled); - -GST_BASE_API -gboolean gst_base_sink_is_qos_enabled (GstBaseSink *sink); - -/* doing async state changes */ - -GST_BASE_API -void gst_base_sink_set_async_enabled (GstBaseSink *sink, gboolean enabled); - -GST_BASE_API -gboolean gst_base_sink_is_async_enabled (GstBaseSink *sink); - -/* tuning synchronisation */ - -GST_BASE_API -void gst_base_sink_set_ts_offset (GstBaseSink *sink, GstClockTimeDiff offset); - -GST_BASE_API -GstClockTimeDiff gst_base_sink_get_ts_offset (GstBaseSink *sink); - -/* last sample */ - -GST_BASE_API -GstSample * gst_base_sink_get_last_sample (GstBaseSink *sink); - -GST_BASE_API -void gst_base_sink_set_last_sample_enabled (GstBaseSink *sink, gboolean enabled); - -GST_BASE_API -gboolean gst_base_sink_is_last_sample_enabled (GstBaseSink *sink); - -/* latency */ - -GST_BASE_API -gboolean gst_base_sink_query_latency (GstBaseSink *sink, gboolean *live, gboolean *upstream_live, - GstClockTime *min_latency, GstClockTime *max_latency); -GST_BASE_API -GstClockTime gst_base_sink_get_latency (GstBaseSink *sink); - -/* render delay */ - -GST_BASE_API -void gst_base_sink_set_render_delay (GstBaseSink *sink, GstClockTime delay); - -GST_BASE_API -GstClockTime gst_base_sink_get_render_delay (GstBaseSink *sink); - -/* blocksize */ - -GST_BASE_API -void gst_base_sink_set_blocksize (GstBaseSink *sink, guint blocksize); - -GST_BASE_API -guint gst_base_sink_get_blocksize (GstBaseSink *sink); - -/* throttle-time */ - -GST_BASE_API -void gst_base_sink_set_throttle_time (GstBaseSink *sink, guint64 throttle); - -GST_BASE_API -guint64 gst_base_sink_get_throttle_time (GstBaseSink *sink); - -/* max-bitrate */ - -GST_BASE_API -void gst_base_sink_set_max_bitrate (GstBaseSink *sink, guint64 max_bitrate); - -GST_BASE_API -guint64 gst_base_sink_get_max_bitrate (GstBaseSink *sink); - -/* processing deadline */ -GST_BASE_API -void gst_base_sink_set_processing_deadline (GstBaseSink *sink, GstClockTime processing_deadline); - -GST_BASE_API -GstClockTime gst_base_sink_get_processing_deadline (GstBaseSink *sink); - -GST_BASE_API -GstClockReturn gst_base_sink_wait_clock (GstBaseSink *sink, GstClockTime time, - GstClockTimeDiff * jitter); -GST_BASE_API -GstFlowReturn gst_base_sink_wait (GstBaseSink *sink, GstClockTime time, - GstClockTimeDiff *jitter); - -GST_BASE_API -GstStructure *gst_base_sink_get_stats (GstBaseSink * sink); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstBaseSink, gst_object_unref) - -G_END_DECLS - -#endif /* __GST_BASE_SINK_H__ */ diff --git a/libs/gst/base/gstbasesrc.c b/libs/gst/base/gstbasesrc.c deleted file mode 100644 index 2ba04cbcb4..0000000000 --- a/libs/gst/base/gstbasesrc.c +++ /dev/null @@ -1,4139 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> - * 2000,2005 Wim Taymans <wim@fluendo.com> - * - * gstbasesrc.c: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * SECTION:gstbasesrc - * @title: GstBaseSrc - * @short_description: Base class for getrange based source elements - * @see_also: #GstPushSrc, #GstBaseTransform, #GstBaseSink - * - * This is a generic base class for source elements. The following - * types of sources are supported: - * - * * random access sources like files - * * seekable sources - * * live sources - * - * The source can be configured to operate in any #GstFormat with the - * gst_base_src_set_format() method. The currently set format determines - * the format of the internal #GstSegment and any %GST_EVENT_SEGMENT - * events. The default format for #GstBaseSrc is %GST_FORMAT_BYTES. - * - * #GstBaseSrc always supports push mode scheduling. If the following - * conditions are met, it also supports pull mode scheduling: - * - * * The format is set to %GST_FORMAT_BYTES (default). - * * #GstBaseSrcClass::is_seekable returns %TRUE. - * - * If all the conditions are met for operating in pull mode, #GstBaseSrc is - * automatically seekable in push mode as well. The following conditions must - * be met to make the element seekable in push mode when the format is not - * %GST_FORMAT_BYTES: - * - * * #GstBaseSrcClass::is_seekable returns %TRUE. - * * #GstBaseSrcClass::query can convert all supported seek formats to the - * internal format as set with gst_base_src_set_format(). - * * #GstBaseSrcClass::do_seek is implemented, performs the seek and returns - * %TRUE. - * - * When the element does not meet the requirements to operate in pull mode, the - * offset and length in the #GstBaseSrcClass::create method should be ignored. - * It is recommended to subclass #GstPushSrc instead, in this situation. If the - * element can operate in pull mode but only with specific offsets and - * lengths, it is allowed to generate an error when the wrong values are passed - * to the #GstBaseSrcClass::create function. - * - * #GstBaseSrc has support for live sources. Live sources are sources that when - * paused discard data, such as audio or video capture devices. A typical live - * source also produces data at a fixed rate and thus provides a clock to publish - * this rate. - * Use gst_base_src_set_live() to activate the live source mode. - * - * A live source does not produce data in the PAUSED state. This means that the - * #GstBaseSrcClass::create method will not be called in PAUSED but only in - * PLAYING. To signal the pipeline that the element will not produce data, the - * return value from the READY to PAUSED state will be - * %GST_STATE_CHANGE_NO_PREROLL. - * - * A typical live source will timestamp the buffers it creates with the - * current running time of the pipeline. This is one reason why a live source - * can only produce data in the PLAYING state, when the clock is actually - * distributed and running. - * - * Live sources that synchronize and block on the clock (an audio source, for - * example) can use gst_base_src_wait_playing() when the - * #GstBaseSrcClass::create function was interrupted by a state change to - * PAUSED. - * - * The #GstBaseSrcClass::get_times method can be used to implement pseudo-live - * sources. It only makes sense to implement the #GstBaseSrcClass::get_times - * function if the source is a live source. The #GstBaseSrcClass::get_times - * function should return timestamps starting from 0, as if it were a non-live - * source. The base class will make sure that the timestamps are transformed - * into the current running_time. The base source will then wait for the - * calculated running_time before pushing out the buffer. - * - * For live sources, the base class will by default report a latency of 0. - * For pseudo live sources, the base class will by default measure the difference - * between the first buffer timestamp and the start time of get_times and will - * report this value as the latency. - * Subclasses should override the query function when this behaviour is not - * acceptable. - * - * There is only support in #GstBaseSrc for exactly one source pad, which - * should be named "src". A source implementation (subclass of #GstBaseSrc) - * should install a pad template in its class_init function, like so: - * |[<!-- language="C" --> - * static void - * my_element_class_init (GstMyElementClass *klass) - * { - * GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); - * // srctemplate should be a #GstStaticPadTemplate with direction - * // %GST_PAD_SRC and name "src" - * gst_element_class_add_static_pad_template (gstelement_class, &srctemplate); - * - * gst_element_class_set_static_metadata (gstelement_class, - * "Source name", - * "Source", - * "My Source element", - * "The author <my.sink@my.email>"); - * } - * ]| - * - * ## Controlled shutdown of live sources in applications - * - * Applications that record from a live source may want to stop recording - * in a controlled way, so that the recording is stopped, but the data - * already in the pipeline is processed to the end (remember that many live - * sources would go on recording forever otherwise). For that to happen the - * application needs to make the source stop recording and send an EOS - * event down the pipeline. The application would then wait for an - * EOS message posted on the pipeline's bus to know when all data has - * been processed and the pipeline can safely be stopped. - * - * An application may send an EOS event to a source element to make it - * perform the EOS logic (send EOS event downstream or post a - * %GST_MESSAGE_SEGMENT_DONE on the bus). This can typically be done - * with the gst_element_send_event() function on the element or its parent bin. - * - * After the EOS has been sent to the element, the application should wait for - * an EOS message to be posted on the pipeline's bus. Once this EOS message is - * received, it may safely shut down the entire pipeline. - * - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include <stdlib.h> -#include <string.h> - -#include <gst/gst_private.h> -#include <gst/glib-compat-private.h> - -#include "gstbasesrc.h" -#include <gst/gst-i18n-lib.h> - -GST_DEBUG_CATEGORY_STATIC (gst_base_src_debug); -#define GST_CAT_DEFAULT gst_base_src_debug - -#define GST_LIVE_GET_LOCK(elem) (&GST_BASE_SRC_CAST(elem)->live_lock) -#define GST_LIVE_LOCK(elem) g_mutex_lock(GST_LIVE_GET_LOCK(elem)) -#define GST_LIVE_TRYLOCK(elem) g_mutex_trylock(GST_LIVE_GET_LOCK(elem)) -#define GST_LIVE_UNLOCK(elem) g_mutex_unlock(GST_LIVE_GET_LOCK(elem)) -#define GST_LIVE_GET_COND(elem) (&GST_BASE_SRC_CAST(elem)->live_cond) -#define GST_LIVE_WAIT(elem) g_cond_wait (GST_LIVE_GET_COND (elem), GST_LIVE_GET_LOCK (elem)) -#define GST_LIVE_WAIT_UNTIL(elem, end_time) g_cond_timed_wait (GST_LIVE_GET_COND (elem), GST_LIVE_GET_LOCK (elem), end_time) -#define GST_LIVE_SIGNAL(elem) g_cond_signal (GST_LIVE_GET_COND (elem)); -#define GST_LIVE_BROADCAST(elem) g_cond_broadcast (GST_LIVE_GET_COND (elem)); - - -#define GST_ASYNC_GET_COND(elem) (&GST_BASE_SRC_CAST(elem)->priv->async_cond) -#define GST_ASYNC_WAIT(elem) g_cond_wait (GST_ASYNC_GET_COND (elem), GST_OBJECT_GET_LOCK (elem)) -#define GST_ASYNC_SIGNAL(elem) g_cond_signal (GST_ASYNC_GET_COND (elem)); - -#define CLEAR_PENDING_EOS(bsrc) \ - G_STMT_START { \ - g_atomic_int_set (&bsrc->priv->has_pending_eos, FALSE); \ - gst_event_replace (&bsrc->priv->pending_eos, NULL); \ - } G_STMT_END - - -/* BaseSrc signals and args */ -enum -{ - /* FILL ME */ - LAST_SIGNAL -}; - -#define DEFAULT_BLOCKSIZE 4096 -#define DEFAULT_NUM_BUFFERS -1 -#define DEFAULT_DO_TIMESTAMP FALSE - -enum -{ - PROP_0, - PROP_BLOCKSIZE, - PROP_NUM_BUFFERS, -#ifndef GST_REMOVE_DEPRECATED - PROP_TYPEFIND, -#endif - PROP_DO_TIMESTAMP -}; - -/* The basesrc implementation need to respect the following locking order: - * 1. STREAM_LOCK - * 2. LIVE_LOCK - * 3. OBJECT_LOCK - */ -struct _GstBaseSrcPrivate -{ - gboolean discont; /* STREAM_LOCK */ - gboolean flushing; /* LIVE_LOCK */ - - GstFlowReturn start_result; /* OBJECT_LOCK */ - gboolean async; /* OBJECT_LOCK */ - - /* if a stream-start event should be sent */ - gboolean stream_start_pending; /* STREAM_LOCK */ - - /* if segment should be sent and a - * seqnum if it was originated by a seek */ - gboolean segment_pending; /* OBJECT_LOCK */ - guint32 segment_seqnum; /* OBJECT_LOCK */ - - /* if EOS is pending (atomic) */ - GstEvent *pending_eos; /* OBJECT_LOCK */ - gint has_pending_eos; /* atomic */ - - /* if the eos was caused by a forced eos from the application */ - gboolean forced_eos; /* LIVE_LOCK */ - - /* startup latency is the time it takes between going to PLAYING and producing - * the first BUFFER with running_time 0. This value is included in the latency - * reporting. */ - GstClockTime latency; /* OBJECT_LOCK */ - /* timestamp offset, this is the offset add to the values of gst_times for - * pseudo live sources */ - GstClockTimeDiff ts_offset; /* OBJECT_LOCK */ - - gboolean do_timestamp; /* OBJECT_LOCK */ - gint dynamic_size; /* atomic */ - gint automatic_eos; /* atomic */ - - /* stream sequence number */ - guint32 seqnum; /* STREAM_LOCK */ - - /* pending events (TAG, CUSTOM_BOTH, CUSTOM_DOWNSTREAM) to be - * pushed in the data stream */ - GList *pending_events; /* OBJECT_LOCK */ - gint have_events; /* OBJECT_LOCK */ - - /* QoS *//* with LOCK */ - gdouble proportion; /* OBJECT_LOCK */ - GstClockTime earliest_time; /* OBJECT_LOCK */ - - GstBufferPool *pool; /* OBJECT_LOCK */ - GstAllocator *allocator; /* OBJECT_LOCK */ - GstAllocationParams params; /* OBJECT_LOCK */ - - GCond async_cond; /* OBJECT_LOCK */ - - /* for _submit_buffer_list() */ - GstBufferList *pending_bufferlist; -}; - -#define BASE_SRC_HAS_PENDING_BUFFER_LIST(src) \ - ((src)->priv->pending_bufferlist != NULL) - -static GstElementClass *parent_class = NULL; -static gint private_offset = 0; - -static void gst_base_src_class_init (GstBaseSrcClass * klass); -static void gst_base_src_init (GstBaseSrc * src, gpointer g_class); -static void gst_base_src_finalize (GObject * object); - - -GType -gst_base_src_get_type (void) -{ - static gsize base_src_type = 0; - - if (g_once_init_enter (&base_src_type)) { - GType _type; - static const GTypeInfo base_src_info = { - sizeof (GstBaseSrcClass), - NULL, - NULL, - (GClassInitFunc) gst_base_src_class_init, - NULL, - NULL, - sizeof (GstBaseSrc), - 0, - (GInstanceInitFunc) gst_base_src_init, - }; - - _type = g_type_register_static (GST_TYPE_ELEMENT, - "GstBaseSrc", &base_src_info, G_TYPE_FLAG_ABSTRACT); - - private_offset = - g_type_add_instance_private (_type, sizeof (GstBaseSrcPrivate)); - - g_once_init_leave (&base_src_type, _type); - } - return base_src_type; -} - -static inline GstBaseSrcPrivate * -gst_base_src_get_instance_private (GstBaseSrc * self) -{ - return (G_STRUCT_MEMBER_P (self, private_offset)); -} - -static GstCaps *gst_base_src_default_get_caps (GstBaseSrc * bsrc, - GstCaps * filter); -static GstCaps *gst_base_src_default_fixate (GstBaseSrc * src, GstCaps * caps); -static GstCaps *gst_base_src_fixate (GstBaseSrc * src, GstCaps * caps); - -static gboolean gst_base_src_is_random_access (GstBaseSrc * src); -static gboolean gst_base_src_activate_mode (GstPad * pad, GstObject * parent, - GstPadMode mode, gboolean active); -static void gst_base_src_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_base_src_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static gboolean gst_base_src_event (GstPad * pad, GstObject * parent, - GstEvent * event); -static gboolean gst_base_src_send_event (GstElement * elem, GstEvent * event); -static gboolean gst_base_src_default_event (GstBaseSrc * src, GstEvent * event); - -static gboolean gst_base_src_query (GstPad * pad, GstObject * parent, - GstQuery * query); - -static void gst_base_src_set_pool_flushing (GstBaseSrc * basesrc, - gboolean flushing); -static gboolean gst_base_src_default_negotiate (GstBaseSrc * basesrc); -static gboolean gst_base_src_default_do_seek (GstBaseSrc * src, - GstSegment * segment); -static gboolean gst_base_src_default_query (GstBaseSrc * src, GstQuery * query); -static gboolean gst_base_src_default_prepare_seek_segment (GstBaseSrc * src, - GstEvent * event, GstSegment * segment); -static GstFlowReturn gst_base_src_default_create (GstBaseSrc * basesrc, - guint64 offset, guint size, GstBuffer ** buf); -static GstFlowReturn gst_base_src_default_alloc (GstBaseSrc * basesrc, - guint64 offset, guint size, GstBuffer ** buf); -static gboolean gst_base_src_decide_allocation_default (GstBaseSrc * basesrc, - GstQuery * query); - -static gboolean gst_base_src_set_flushing (GstBaseSrc * basesrc, - gboolean flushing); - -static gboolean gst_base_src_start (GstBaseSrc * basesrc); -static gboolean gst_base_src_stop (GstBaseSrc * basesrc); - -static GstStateChangeReturn gst_base_src_change_state (GstElement * element, - GstStateChange transition); - -static void gst_base_src_loop (GstPad * pad); -static GstFlowReturn gst_base_src_getrange (GstPad * pad, GstObject * parent, - guint64 offset, guint length, GstBuffer ** buf); -static GstFlowReturn gst_base_src_get_range (GstBaseSrc * src, guint64 offset, - guint length, GstBuffer ** buf); -static gboolean gst_base_src_seekable (GstBaseSrc * src); -static gboolean gst_base_src_negotiate_unlocked (GstBaseSrc * basesrc); -static gboolean gst_base_src_update_length (GstBaseSrc * src, guint64 offset, - guint * length, gboolean force); - -static void -gst_base_src_class_init (GstBaseSrcClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - gobject_class = G_OBJECT_CLASS (klass); - gstelement_class = GST_ELEMENT_CLASS (klass); - - if (private_offset != 0) - g_type_class_adjust_private_offset (klass, &private_offset); - - GST_DEBUG_CATEGORY_INIT (gst_base_src_debug, "basesrc", 0, "basesrc element"); - - parent_class = g_type_class_peek_parent (klass); - - gobject_class->finalize = gst_base_src_finalize; - gobject_class->set_property = gst_base_src_set_property; - gobject_class->get_property = gst_base_src_get_property; - - g_object_class_install_property (gobject_class, PROP_BLOCKSIZE, - g_param_spec_uint ("blocksize", "Block size", - "Size in bytes to read per buffer (-1 = default)", 0, G_MAXUINT, - DEFAULT_BLOCKSIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_NUM_BUFFERS, - g_param_spec_int ("num-buffers", "num-buffers", - "Number of buffers to output before sending EOS (-1 = unlimited)", - -1, G_MAXINT, DEFAULT_NUM_BUFFERS, G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); -#ifndef GST_REMOVE_DEPRECATED - g_object_class_install_property (gobject_class, PROP_TYPEFIND, - g_param_spec_boolean ("typefind", "Typefind", - "Run typefind before negotiating (deprecated, non-functional)", FALSE, - G_PARAM_READWRITE | G_PARAM_DEPRECATED | G_PARAM_STATIC_STRINGS)); -#endif - g_object_class_install_property (gobject_class, PROP_DO_TIMESTAMP, - g_param_spec_boolean ("do-timestamp", "Do timestamp", - "Apply current stream time to buffers", DEFAULT_DO_TIMESTAMP, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_base_src_change_state); - gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_base_src_send_event); - - klass->get_caps = GST_DEBUG_FUNCPTR (gst_base_src_default_get_caps); - klass->negotiate = GST_DEBUG_FUNCPTR (gst_base_src_default_negotiate); - klass->fixate = GST_DEBUG_FUNCPTR (gst_base_src_default_fixate); - klass->prepare_seek_segment = - GST_DEBUG_FUNCPTR (gst_base_src_default_prepare_seek_segment); - klass->do_seek = GST_DEBUG_FUNCPTR (gst_base_src_default_do_seek); - klass->query = GST_DEBUG_FUNCPTR (gst_base_src_default_query); - klass->event = GST_DEBUG_FUNCPTR (gst_base_src_default_event); - klass->create = GST_DEBUG_FUNCPTR (gst_base_src_default_create); - klass->alloc = GST_DEBUG_FUNCPTR (gst_base_src_default_alloc); - klass->decide_allocation = - GST_DEBUG_FUNCPTR (gst_base_src_decide_allocation_default); - - /* Registering debug symbols for function pointers */ - GST_DEBUG_REGISTER_FUNCPTR (gst_base_src_activate_mode); - GST_DEBUG_REGISTER_FUNCPTR (gst_base_src_event); - GST_DEBUG_REGISTER_FUNCPTR (gst_base_src_query); - GST_DEBUG_REGISTER_FUNCPTR (gst_base_src_getrange); - GST_DEBUG_REGISTER_FUNCPTR (gst_base_src_fixate); -} - -static void -gst_base_src_init (GstBaseSrc * basesrc, gpointer g_class) -{ - GstPad *pad; - GstPadTemplate *pad_template; - - basesrc->priv = gst_base_src_get_instance_private (basesrc); - - basesrc->is_live = FALSE; - g_mutex_init (&basesrc->live_lock); - g_cond_init (&basesrc->live_cond); - basesrc->num_buffers = DEFAULT_NUM_BUFFERS; - basesrc->num_buffers_left = -1; - g_atomic_int_set (&basesrc->priv->automatic_eos, TRUE); - - basesrc->can_activate_push = TRUE; - - pad_template = - gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src"); - g_return_if_fail (pad_template != NULL); - - GST_DEBUG_OBJECT (basesrc, "creating src pad"); - pad = gst_pad_new_from_template (pad_template, "src"); - - GST_DEBUG_OBJECT (basesrc, "setting functions on src pad"); - gst_pad_set_activatemode_function (pad, gst_base_src_activate_mode); - gst_pad_set_event_function (pad, gst_base_src_event); - gst_pad_set_query_function (pad, gst_base_src_query); - gst_pad_set_getrange_function (pad, gst_base_src_getrange); - - /* hold pointer to pad */ - basesrc->srcpad = pad; - GST_DEBUG_OBJECT (basesrc, "adding src pad"); - gst_element_add_pad (GST_ELEMENT (basesrc), pad); - - basesrc->blocksize = DEFAULT_BLOCKSIZE; - basesrc->clock_id = NULL; - /* we operate in BYTES by default */ - gst_base_src_set_format (basesrc, GST_FORMAT_BYTES); - basesrc->priv->do_timestamp = DEFAULT_DO_TIMESTAMP; - g_atomic_int_set (&basesrc->priv->have_events, FALSE); - - g_cond_init (&basesrc->priv->async_cond); - basesrc->priv->start_result = GST_FLOW_FLUSHING; - GST_OBJECT_FLAG_UNSET (basesrc, GST_BASE_SRC_FLAG_STARTED); - GST_OBJECT_FLAG_UNSET (basesrc, GST_BASE_SRC_FLAG_STARTING); - GST_OBJECT_FLAG_SET (basesrc, GST_ELEMENT_FLAG_SOURCE); - - GST_DEBUG_OBJECT (basesrc, "init done"); -} - -static void -gst_base_src_finalize (GObject * object) -{ - GstBaseSrc *basesrc; - GstEvent **event_p; - - basesrc = GST_BASE_SRC (object); - - g_mutex_clear (&basesrc->live_lock); - g_cond_clear (&basesrc->live_cond); - g_cond_clear (&basesrc->priv->async_cond); - - event_p = &basesrc->pending_seek; - gst_event_replace (event_p, NULL); - - if (basesrc->priv->pending_events) { - g_list_foreach (basesrc->priv->pending_events, (GFunc) gst_event_unref, - NULL); - g_list_free (basesrc->priv->pending_events); - } - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -/* Call with LIVE_LOCK held */ -static GstFlowReturn -gst_base_src_wait_playing_unlocked (GstBaseSrc * src) -{ - while (G_UNLIKELY (!src->live_running && !src->priv->flushing)) { - /* block until the state changes, or we get a flush, or something */ - GST_DEBUG_OBJECT (src, "live source waiting for running state"); - GST_LIVE_WAIT (src); - GST_DEBUG_OBJECT (src, "live source unlocked"); - } - - if (src->priv->flushing) - goto flushing; - - return GST_FLOW_OK; - - /* ERRORS */ -flushing: - { - GST_DEBUG_OBJECT (src, "we are flushing"); - return GST_FLOW_FLUSHING; - } -} - - -/** - * gst_base_src_wait_playing: - * @src: the src - * - * If the #GstBaseSrcClass::create method performs its own synchronisation - * against the clock it must unblock when going from PLAYING to the PAUSED state - * and call this method before continuing to produce the remaining data. - * - * This function will block until a state change to PLAYING happens (in which - * case this function returns %GST_FLOW_OK) or the processing must be stopped due - * to a state change to READY or a FLUSH event (in which case this function - * returns %GST_FLOW_FLUSHING). - * - * Returns: %GST_FLOW_OK if @src is PLAYING and processing can - * continue. Any other return value should be returned from the create vmethod. - */ -GstFlowReturn -gst_base_src_wait_playing (GstBaseSrc * src) -{ - GstFlowReturn ret; - - g_return_val_if_fail (GST_IS_BASE_SRC (src), GST_FLOW_ERROR); - - GST_LIVE_LOCK (src); - ret = gst_base_src_wait_playing_unlocked (src); - GST_LIVE_UNLOCK (src); - - return ret; -} - -/** - * gst_base_src_set_live: - * @src: base source instance - * @live: new live-mode - * - * If the element listens to a live source, @live should - * be set to %TRUE. - * - * A live source will not produce data in the PAUSED state and - * will therefore not be able to participate in the PREROLL phase - * of a pipeline. To signal this fact to the application and the - * pipeline, the state change return value of the live source will - * be GST_STATE_CHANGE_NO_PREROLL. - */ -void -gst_base_src_set_live (GstBaseSrc * src, gboolean live) -{ - g_return_if_fail (GST_IS_BASE_SRC (src)); - - GST_OBJECT_LOCK (src); - src->is_live = live; - GST_OBJECT_UNLOCK (src); -} - -/** - * gst_base_src_is_live: - * @src: base source instance - * - * Check if an element is in live mode. - * - * Returns: %TRUE if element is in live mode. - */ -gboolean -gst_base_src_is_live (GstBaseSrc * src) -{ - gboolean result; - - g_return_val_if_fail (GST_IS_BASE_SRC (src), FALSE); - - GST_OBJECT_LOCK (src); - result = src->is_live; - GST_OBJECT_UNLOCK (src); - - return result; -} - -/** - * gst_base_src_set_format: - * @src: base source instance - * @format: the format to use - * - * Sets the default format of the source. This will be the format used - * for sending SEGMENT events and for performing seeks. - * - * If a format of GST_FORMAT_BYTES is set, the element will be able to - * operate in pull mode if the #GstBaseSrcClass::is_seekable returns %TRUE. - * - * This function must only be called in states < %GST_STATE_PAUSED. - */ -void -gst_base_src_set_format (GstBaseSrc * src, GstFormat format) -{ - g_return_if_fail (GST_IS_BASE_SRC (src)); - g_return_if_fail (GST_STATE (src) <= GST_STATE_READY); - - GST_OBJECT_LOCK (src); - gst_segment_init (&src->segment, format); - GST_OBJECT_UNLOCK (src); -} - -/** - * gst_base_src_set_dynamic_size: - * @src: base source instance - * @dynamic: new dynamic size mode - * - * If not @dynamic, size is only updated when needed, such as when trying to - * read past current tracked size. Otherwise, size is checked for upon each - * read. - */ -void -gst_base_src_set_dynamic_size (GstBaseSrc * src, gboolean dynamic) -{ - g_return_if_fail (GST_IS_BASE_SRC (src)); - - g_atomic_int_set (&src->priv->dynamic_size, dynamic); -} - -/** - * gst_base_src_set_automatic_eos: - * @src: base source instance - * @automatic_eos: automatic eos - * - * If @automatic_eos is %TRUE, @src will automatically go EOS if a buffer - * after the total size is returned. By default this is %TRUE but sources - * that can't return an authoritative size and only know that they're EOS - * when trying to read more should set this to %FALSE. - * - * When @src operates in %GST_FORMAT_TIME, #GstBaseSrc will send an EOS - * when a buffer outside of the currently configured segment is pushed if - * @automatic_eos is %TRUE. Since 1.16, if @automatic_eos is %FALSE an - * EOS will be pushed only when the #GstBaseSrcClass::create implementation - * returns %GST_FLOW_EOS. - * - * Since: 1.4 - */ -void -gst_base_src_set_automatic_eos (GstBaseSrc * src, gboolean automatic_eos) -{ - g_return_if_fail (GST_IS_BASE_SRC (src)); - - g_atomic_int_set (&src->priv->automatic_eos, automatic_eos); -} - -/** - * gst_base_src_set_async: - * @src: base source instance - * @async: new async mode - * - * Configure async behaviour in @src, no state change will block. The open, - * close, start, stop, play and pause virtual methods will be executed in a - * different thread and are thus allowed to perform blocking operations. Any - * blocking operation should be unblocked with the unlock vmethod. - */ -void -gst_base_src_set_async (GstBaseSrc * src, gboolean async) -{ - g_return_if_fail (GST_IS_BASE_SRC (src)); - - GST_OBJECT_LOCK (src); - src->priv->async = async; - GST_OBJECT_UNLOCK (src); -} - -/** - * gst_base_src_is_async: - * @src: base source instance - * - * Get the current async behaviour of @src. See also gst_base_src_set_async(). - * - * Returns: %TRUE if @src is operating in async mode. - */ -gboolean -gst_base_src_is_async (GstBaseSrc * src) -{ - gboolean res; - - g_return_val_if_fail (GST_IS_BASE_SRC (src), FALSE); - - GST_OBJECT_LOCK (src); - res = src->priv->async; - GST_OBJECT_UNLOCK (src); - - return res; -} - - -/** - * gst_base_src_query_latency: - * @src: the source - * @live: (out) (allow-none): if the source is live - * @min_latency: (out) (allow-none): the min latency of the source - * @max_latency: (out) (allow-none): the max latency of the source - * - * Query the source for the latency parameters. @live will be %TRUE when @src is - * configured as a live source. @min_latency and @max_latency will be set - * to the difference between the running time and the timestamp of the first - * buffer. - * - * This function is mostly used by subclasses. - * - * Returns: %TRUE if the query succeeded. - */ -gboolean -gst_base_src_query_latency (GstBaseSrc * src, gboolean * live, - GstClockTime * min_latency, GstClockTime * max_latency) -{ - GstClockTime min; - - g_return_val_if_fail (GST_IS_BASE_SRC (src), FALSE); - - GST_OBJECT_LOCK (src); - if (live) - *live = src->is_live; - - /* if we have a startup latency, report this one, else report 0. Subclasses - * are supposed to override the query function if they want something - * else. */ - if (src->priv->latency != -1) - min = src->priv->latency; - else - min = 0; - - if (min_latency) - *min_latency = min; - if (max_latency) - *max_latency = min; - - GST_LOG_OBJECT (src, "latency: live %d, min %" GST_TIME_FORMAT - ", max %" GST_TIME_FORMAT, src->is_live, GST_TIME_ARGS (min), - GST_TIME_ARGS (min)); - GST_OBJECT_UNLOCK (src); - - return TRUE; -} - -/** - * gst_base_src_set_blocksize: - * @src: the source - * @blocksize: the new blocksize in bytes - * - * Set the number of bytes that @src will push out with each buffer. When - * @blocksize is set to -1, a default length will be used. - */ -void -gst_base_src_set_blocksize (GstBaseSrc * src, guint blocksize) -{ - g_return_if_fail (GST_IS_BASE_SRC (src)); - - GST_OBJECT_LOCK (src); - src->blocksize = blocksize; - GST_OBJECT_UNLOCK (src); -} - -/** - * gst_base_src_get_blocksize: - * @src: the source - * - * Get the number of bytes that @src will push out with each buffer. - * - * Returns: the number of bytes pushed with each buffer. - */ -guint -gst_base_src_get_blocksize (GstBaseSrc * src) -{ - gint res; - - g_return_val_if_fail (GST_IS_BASE_SRC (src), 0); - - GST_OBJECT_LOCK (src); - res = src->blocksize; - GST_OBJECT_UNLOCK (src); - - return res; -} - - -/** - * gst_base_src_set_do_timestamp: - * @src: the source - * @timestamp: enable or disable timestamping - * - * Configure @src to automatically timestamp outgoing buffers based on the - * current running_time of the pipeline. This property is mostly useful for live - * sources. - */ -void -gst_base_src_set_do_timestamp (GstBaseSrc * src, gboolean timestamp) -{ - g_return_if_fail (GST_IS_BASE_SRC (src)); - - GST_OBJECT_LOCK (src); - src->priv->do_timestamp = timestamp; - if (timestamp && src->segment.format != GST_FORMAT_TIME) - gst_segment_init (&src->segment, GST_FORMAT_TIME); - GST_OBJECT_UNLOCK (src); -} - -/** - * gst_base_src_get_do_timestamp: - * @src: the source - * - * Query if @src timestamps outgoing buffers based on the current running_time. - * - * Returns: %TRUE if the base class will automatically timestamp outgoing buffers. - */ -gboolean -gst_base_src_get_do_timestamp (GstBaseSrc * src) -{ - gboolean res; - - g_return_val_if_fail (GST_IS_BASE_SRC (src), FALSE); - - GST_OBJECT_LOCK (src); - res = src->priv->do_timestamp; - GST_OBJECT_UNLOCK (src); - - return res; -} - -/** - * gst_base_src_new_seamless_segment: - * @src: The source - * @start: The new start value for the segment - * @stop: Stop value for the new segment - * @time: The new time value for the start of the new segment - * - * Prepare a new seamless segment for emission downstream. This function must - * only be called by derived sub-classes, and only from the #GstBaseSrcClass::create function, - * as the stream-lock needs to be held. - * - * The format for the new segment will be the current format of the source, as - * configured with gst_base_src_set_format() - * - * Returns: %TRUE if preparation of the seamless segment succeeded. - * - * Deprecated: 1.18: Use gst_base_src_new_segment() - */ -gboolean -gst_base_src_new_seamless_segment (GstBaseSrc * src, gint64 start, gint64 stop, - gint64 time) -{ - gboolean res = TRUE; - - GST_OBJECT_LOCK (src); - - src->segment.base = gst_segment_to_running_time (&src->segment, - src->segment.format, src->segment.position); - src->segment.position = src->segment.start = start; - src->segment.stop = stop; - src->segment.time = time; - - /* Mark pending segment. Will be sent before next data */ - src->priv->segment_pending = TRUE; - src->priv->segment_seqnum = gst_util_seqnum_next (); - - GST_DEBUG_OBJECT (src, - "Starting new seamless segment. Start %" GST_TIME_FORMAT " stop %" - GST_TIME_FORMAT " time %" GST_TIME_FORMAT " base %" GST_TIME_FORMAT, - GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (time), - GST_TIME_ARGS (src->segment.base)); - - GST_OBJECT_UNLOCK (src); - - src->priv->discont = TRUE; - src->running = TRUE; - - return res; -} - -/** - * gst_base_src_new_segment: - * @src: a #GstBaseSrc - * @segment: a pointer to a #GstSegment - * - * Prepare a new segment for emission downstream. This function must - * only be called by derived sub-classes, and only from the #GstBaseSrcClass::create function, - * as the stream-lock needs to be held. - * - * The format for the @segment must be identical with the current format - * of the source, as configured with gst_base_src_set_format(). - * - * The format of @src must not be %GST_FORMAT_UNDEFINED and the format - * should be configured via gst_base_src_set_format() before calling this method. - * - * Returns: %TRUE if preparation of new segment succeeded. - * - * Since: 1.18 - */ -gboolean -gst_base_src_new_segment (GstBaseSrc * src, const GstSegment * segment) -{ - g_return_val_if_fail (GST_IS_BASE_SRC (src), FALSE); - g_return_val_if_fail (segment != NULL, FALSE); - - GST_OBJECT_LOCK (src); - - if (src->segment.format == GST_FORMAT_UNDEFINED) { - /* subclass must set valid format before calling this method */ - GST_WARNING_OBJECT (src, "segment format is not configured yet, ignore"); - GST_OBJECT_UNLOCK (src); - return FALSE; - } - - if (src->segment.format != segment->format) { - GST_WARNING_OBJECT (src, "segment format mismatched, ignore"); - GST_OBJECT_UNLOCK (src); - return FALSE; - } - - gst_segment_copy_into (segment, &src->segment); - - /* Mark pending segment. Will be sent before next data */ - src->priv->segment_pending = TRUE; - src->priv->segment_seqnum = gst_util_seqnum_next (); - - GST_DEBUG_OBJECT (src, "Starting new segment %" GST_SEGMENT_FORMAT, segment); - - GST_OBJECT_UNLOCK (src); - - src->running = TRUE; - - return TRUE; -} - -/* called with STREAM_LOCK */ -static gboolean -gst_base_src_send_stream_start (GstBaseSrc * src) -{ - gboolean ret = TRUE; - - if (src->priv->stream_start_pending) { - gchar *stream_id; - GstEvent *event; - - stream_id = - gst_pad_create_stream_id (src->srcpad, GST_ELEMENT_CAST (src), NULL); - - GST_DEBUG_OBJECT (src, "Pushing STREAM_START"); - event = gst_event_new_stream_start (stream_id); - gst_event_set_group_id (event, gst_util_group_id_next ()); - - ret = gst_pad_push_event (src->srcpad, event); - src->priv->stream_start_pending = FALSE; - g_free (stream_id); - } - - return ret; -} - -/** - * gst_base_src_set_caps: - * @src: a #GstBaseSrc - * @caps: (transfer none): a #GstCaps - * - * Set new caps on the basesrc source pad. - * - * Returns: %TRUE if the caps could be set - */ -gboolean -gst_base_src_set_caps (GstBaseSrc * src, GstCaps * caps) -{ - GstBaseSrcClass *bclass; - gboolean res = TRUE; - GstCaps *current_caps; - - bclass = GST_BASE_SRC_GET_CLASS (src); - - gst_base_src_send_stream_start (src); - - current_caps = gst_pad_get_current_caps (GST_BASE_SRC_PAD (src)); - if (current_caps && gst_caps_is_equal (current_caps, caps)) { - GST_DEBUG_OBJECT (src, "New caps equal to old ones: %" GST_PTR_FORMAT, - caps); - res = TRUE; - } else { - if (bclass->set_caps) - res = bclass->set_caps (src, caps); - - if (res) - res = gst_pad_push_event (src->srcpad, gst_event_new_caps (caps)); - } - - if (current_caps) - gst_caps_unref (current_caps); - - return res; -} - -static GstCaps * -gst_base_src_default_get_caps (GstBaseSrc * bsrc, GstCaps * filter) -{ - GstCaps *caps = NULL; - GstPadTemplate *pad_template; - GstBaseSrcClass *bclass; - - bclass = GST_BASE_SRC_GET_CLASS (bsrc); - - pad_template = - gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "src"); - - if (pad_template != NULL) { - caps = gst_pad_template_get_caps (pad_template); - - if (filter) { - GstCaps *intersection; - - intersection = - gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST); - gst_caps_unref (caps); - caps = intersection; - } - } - return caps; -} - -static GstCaps * -gst_base_src_default_fixate (GstBaseSrc * bsrc, GstCaps * caps) -{ - GST_DEBUG_OBJECT (bsrc, "using default caps fixate function"); - return gst_caps_fixate (caps); -} - -static GstCaps * -gst_base_src_fixate (GstBaseSrc * bsrc, GstCaps * caps) -{ - GstBaseSrcClass *bclass; - - bclass = GST_BASE_SRC_GET_CLASS (bsrc); - - if (bclass->fixate) - caps = bclass->fixate (bsrc, caps); - - return caps; -} - -static gboolean -gst_base_src_default_query (GstBaseSrc * src, GstQuery * query) -{ - gboolean res; - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - { - GstFormat format; - - gst_query_parse_position (query, &format, NULL); - - GST_DEBUG_OBJECT (src, "position query in format %s", - gst_format_get_name (format)); - - switch (format) { - case GST_FORMAT_PERCENT: - { - gint64 percent; - gint64 position; - gint64 duration; - - GST_OBJECT_LOCK (src); - position = src->segment.position; - duration = src->segment.duration; - GST_OBJECT_UNLOCK (src); - - if (position != -1 && duration != -1) { - if (position < duration) - percent = gst_util_uint64_scale (GST_FORMAT_PERCENT_MAX, position, - duration); - else - percent = GST_FORMAT_PERCENT_MAX; - } else - percent = -1; - - gst_query_set_position (query, GST_FORMAT_PERCENT, percent); - res = TRUE; - break; - } - default: - { - gint64 position; - GstFormat seg_format; - - GST_OBJECT_LOCK (src); - position = - gst_segment_to_stream_time (&src->segment, src->segment.format, - src->segment.position); - seg_format = src->segment.format; - GST_OBJECT_UNLOCK (src); - - if (position != -1) { - /* convert to requested format */ - res = - gst_pad_query_convert (src->srcpad, seg_format, - position, format, &position); - } else - res = TRUE; - - if (res) - gst_query_set_position (query, format, position); - - break; - } - } - break; - } - case GST_QUERY_DURATION: - { - GstFormat format; - - gst_query_parse_duration (query, &format, NULL); - - GST_DEBUG_OBJECT (src, "duration query in format %s", - gst_format_get_name (format)); - - switch (format) { - case GST_FORMAT_PERCENT: - gst_query_set_duration (query, GST_FORMAT_PERCENT, - GST_FORMAT_PERCENT_MAX); - res = TRUE; - break; - default: - { - gint64 duration; - GstFormat seg_format; - guint length = 0; - - /* may have to refresh duration */ - gst_base_src_update_length (src, 0, &length, - g_atomic_int_get (&src->priv->dynamic_size)); - - /* this is the duration as configured by the subclass. */ - GST_OBJECT_LOCK (src); - duration = src->segment.duration; - seg_format = src->segment.format; - GST_OBJECT_UNLOCK (src); - - GST_LOG_OBJECT (src, "duration %" G_GINT64_FORMAT ", format %s", - duration, gst_format_get_name (seg_format)); - - if (duration != -1) { - /* convert to requested format, if this fails, we have a duration - * but we cannot answer the query, we must return FALSE. */ - res = - gst_pad_query_convert (src->srcpad, seg_format, - duration, format, &duration); - } else { - /* The subclass did not configure a duration, we assume that the - * media has an unknown duration then and we return TRUE to report - * this. Note that this is not the same as returning FALSE, which - * means that we cannot report the duration at all. */ - res = TRUE; - } - - if (res) - gst_query_set_duration (query, format, duration); - - break; - } - } - break; - } - - case GST_QUERY_SEEKING: - { - GstFormat format, seg_format; - gint64 duration; - - GST_OBJECT_LOCK (src); - duration = src->segment.duration; - seg_format = src->segment.format; - GST_OBJECT_UNLOCK (src); - - gst_query_parse_seeking (query, &format, NULL, NULL, NULL); - if (format == seg_format) { - gst_query_set_seeking (query, seg_format, - gst_base_src_seekable (src), 0, duration); - res = TRUE; - } else { - /* FIXME 2.0: return TRUE + seekable=FALSE for SEEKING query here */ - /* Don't reply to the query to make up for demuxers which don't - * handle the SEEKING query yet. Players like Totem will fall back - * to the duration when the SEEKING query isn't answered. */ - res = FALSE; - } - break; - } - case GST_QUERY_SEGMENT: - { - GstFormat format; - gint64 start, stop; - - GST_OBJECT_LOCK (src); - - format = src->segment.format; - - start = - gst_segment_to_stream_time (&src->segment, format, - src->segment.start); - if ((stop = src->segment.stop) == -1) - stop = src->segment.duration; - else - stop = gst_segment_to_stream_time (&src->segment, format, stop); - - gst_query_set_segment (query, src->segment.rate, format, start, stop); - - GST_OBJECT_UNLOCK (src); - res = TRUE; - break; - } - - case GST_QUERY_FORMATS: - { - gst_query_set_formats (query, 3, GST_FORMAT_DEFAULT, - GST_FORMAT_BYTES, GST_FORMAT_PERCENT); - res = TRUE; - break; - } - 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); - - /* we can only convert between equal formats... */ - if (src_fmt == dest_fmt) { - dest_val = src_val; - res = TRUE; - } else - res = FALSE; - - gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); - break; - } - case GST_QUERY_LATENCY: - { - GstClockTime min, max; - gboolean live; - - /* Subclasses should override and implement something useful */ - res = gst_base_src_query_latency (src, &live, &min, &max); - - GST_LOG_OBJECT (src, "report latency: live %d, min %" GST_TIME_FORMAT - ", max %" GST_TIME_FORMAT, live, GST_TIME_ARGS (min), - GST_TIME_ARGS (max)); - - gst_query_set_latency (query, live, min, max); - break; - } - case GST_QUERY_JITTER: - case GST_QUERY_RATE: - res = FALSE; - break; - case GST_QUERY_BUFFERING: - { - GstFormat format, seg_format; - gint64 start, stop, estimated; - - gst_query_parse_buffering_range (query, &format, NULL, NULL, NULL); - - GST_DEBUG_OBJECT (src, "buffering query in format %s", - gst_format_get_name (format)); - - GST_OBJECT_LOCK (src); - if (src->random_access) { - estimated = 0; - start = 0; - if (format == GST_FORMAT_PERCENT) - stop = GST_FORMAT_PERCENT_MAX; - else - stop = src->segment.duration; - } else { - estimated = -1; - start = -1; - stop = -1; - } - seg_format = src->segment.format; - GST_OBJECT_UNLOCK (src); - - /* convert to required format. When the conversion fails, we can't answer - * the query. When the value is unknown, we can don't perform conversion - * but report TRUE. */ - if (format != GST_FORMAT_PERCENT && stop != -1) { - res = gst_pad_query_convert (src->srcpad, seg_format, - stop, format, &stop); - } else { - res = TRUE; - } - if (res && format != GST_FORMAT_PERCENT && start != -1) - res = gst_pad_query_convert (src->srcpad, seg_format, - start, format, &start); - - gst_query_set_buffering_range (query, format, start, stop, estimated); - break; - } - case GST_QUERY_SCHEDULING: - { - gboolean random_access; - - random_access = gst_base_src_is_random_access (src); - - /* we can operate in getrange mode if the native format is bytes - * and we are seekable, this condition is set in the random_access - * flag and is set in the _start() method. */ - gst_query_set_scheduling (query, GST_SCHEDULING_FLAG_SEEKABLE, 1, -1, 0); - if (random_access) - gst_query_add_scheduling_mode (query, GST_PAD_MODE_PULL); - gst_query_add_scheduling_mode (query, GST_PAD_MODE_PUSH); - - res = TRUE; - break; - } - case GST_QUERY_CAPS: - { - GstBaseSrcClass *bclass; - GstCaps *caps, *filter; - - bclass = GST_BASE_SRC_GET_CLASS (src); - if (bclass->get_caps) { - gst_query_parse_caps (query, &filter); - if ((caps = bclass->get_caps (src, filter))) { - gst_query_set_caps_result (query, caps); - gst_caps_unref (caps); - res = TRUE; - } else { - res = FALSE; - } - } else - res = FALSE; - break; - } - case GST_QUERY_URI:{ - if (GST_IS_URI_HANDLER (src)) { - gchar *uri = gst_uri_handler_get_uri (GST_URI_HANDLER (src)); - - if (uri != NULL) { - gst_query_set_uri (query, uri); - g_free (uri); - res = TRUE; - } else { - res = FALSE; - } - } else { - res = FALSE; - } - break; - } - default: - res = FALSE; - break; - } - GST_DEBUG_OBJECT (src, "query %s returns %d", GST_QUERY_TYPE_NAME (query), - res); - - return res; -} - -static gboolean -gst_base_src_query (GstPad * pad, GstObject * parent, GstQuery * query) -{ - GstBaseSrc *src; - GstBaseSrcClass *bclass; - gboolean result = FALSE; - - src = GST_BASE_SRC (parent); - bclass = GST_BASE_SRC_GET_CLASS (src); - - if (bclass->query) - result = bclass->query (src, query); - - return result; -} - -static gboolean -gst_base_src_default_do_seek (GstBaseSrc * src, GstSegment * segment) -{ - gboolean res = TRUE; - - /* update our offset if the start/stop position was updated */ - if (segment->format == GST_FORMAT_BYTES) { - segment->time = segment->start; - } else if (segment->start == 0) { - /* seek to start, we can implement a default for this. */ - segment->time = 0; - } else { - res = FALSE; - GST_INFO_OBJECT (src, "Can't do a default seek"); - } - - return res; -} - -static gboolean -gst_base_src_do_seek (GstBaseSrc * src, GstSegment * segment) -{ - GstBaseSrcClass *bclass; - gboolean result = FALSE; - - bclass = GST_BASE_SRC_GET_CLASS (src); - - GST_INFO_OBJECT (src, "seeking: %" GST_SEGMENT_FORMAT, segment); - - if (bclass->do_seek) - result = bclass->do_seek (src, segment); - - return result; -} - -#define SEEK_TYPE_IS_RELATIVE(t) (((t) != GST_SEEK_TYPE_NONE) && ((t) != GST_SEEK_TYPE_SET)) - -static gboolean -gst_base_src_default_prepare_seek_segment (GstBaseSrc * src, GstEvent * event, - GstSegment * segment) -{ - /* By default, we try one of 2 things: - * - For absolute seek positions, convert the requested position to our - * configured processing format and place it in the output segment \ - * - For relative seek positions, convert our current (input) values to the - * seek format, adjust by the relative seek offset and then convert back to - * the processing format - */ - GstSeekType start_type, stop_type; - gint64 start, stop; - GstSeekFlags flags; - GstFormat seek_format, dest_format; - gdouble rate; - gboolean update; - gboolean res = TRUE; - - gst_event_parse_seek (event, &rate, &seek_format, &flags, - &start_type, &start, &stop_type, &stop); - dest_format = segment->format; - - if (seek_format == dest_format) { - gst_segment_do_seek (segment, rate, seek_format, flags, - start_type, start, stop_type, stop, &update); - return TRUE; - } - - if (start_type != GST_SEEK_TYPE_NONE) { - /* FIXME: Handle seek_end by converting the input segment vals */ - res = - gst_pad_query_convert (src->srcpad, seek_format, start, dest_format, - &start); - start_type = GST_SEEK_TYPE_SET; - } - - if (res && stop_type != GST_SEEK_TYPE_NONE) { - /* FIXME: Handle seek_end by converting the input segment vals */ - res = - gst_pad_query_convert (src->srcpad, seek_format, stop, dest_format, - &stop); - stop_type = GST_SEEK_TYPE_SET; - } - - /* And finally, configure our output segment in the desired format */ - if (res) { - res = - gst_segment_do_seek (segment, rate, dest_format, flags, start_type, - start, stop_type, stop, &update); - } - - if (!res) - goto no_format; - - return res; - -no_format: - { - GST_DEBUG_OBJECT (src, "undefined format given, seek aborted."); - return FALSE; - } -} - -static gboolean -gst_base_src_prepare_seek_segment (GstBaseSrc * src, GstEvent * event, - GstSegment * seeksegment) -{ - GstBaseSrcClass *bclass; - gboolean result = FALSE; - - bclass = GST_BASE_SRC_GET_CLASS (src); - - if (bclass->prepare_seek_segment) - result = bclass->prepare_seek_segment (src, event, seeksegment); - - return result; -} - -static GstFlowReturn -gst_base_src_default_alloc (GstBaseSrc * src, guint64 offset, - guint size, GstBuffer ** buffer) -{ - GstFlowReturn ret; - GstBaseSrcPrivate *priv = src->priv; - GstBufferPool *pool = NULL; - GstAllocator *allocator = NULL; - GstAllocationParams params; - - GST_OBJECT_LOCK (src); - if (priv->pool) { - pool = gst_object_ref (priv->pool); - } else if (priv->allocator) { - allocator = gst_object_ref (priv->allocator); - } - params = priv->params; - GST_OBJECT_UNLOCK (src); - - if (pool) { - ret = gst_buffer_pool_acquire_buffer (pool, buffer, NULL); - } else if (size != -1) { - *buffer = gst_buffer_new_allocate (allocator, size, ¶ms); - if (G_UNLIKELY (*buffer == NULL)) - goto alloc_failed; - - ret = GST_FLOW_OK; - } else { - GST_WARNING_OBJECT (src, "Not trying to alloc %u bytes. Blocksize not set?", - size); - goto alloc_failed; - } - -done: - if (pool) - gst_object_unref (pool); - if (allocator) - gst_object_unref (allocator); - - return ret; - - /* ERRORS */ -alloc_failed: - { - GST_ERROR_OBJECT (src, "Failed to allocate %u bytes", size); - ret = GST_FLOW_ERROR; - goto done; - } -} - -static GstFlowReturn -gst_base_src_default_create (GstBaseSrc * src, guint64 offset, - guint size, GstBuffer ** buffer) -{ - GstBaseSrcClass *bclass; - GstFlowReturn ret; - GstBuffer *res_buf; - - bclass = GST_BASE_SRC_GET_CLASS (src); - - if (G_UNLIKELY (!bclass->alloc)) - goto no_function; - if (G_UNLIKELY (!bclass->fill)) - goto no_function; - - if (*buffer == NULL) { - /* downstream did not provide us with a buffer to fill, allocate one - * ourselves */ - ret = bclass->alloc (src, offset, size, &res_buf); - if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto alloc_failed; - } else { - res_buf = *buffer; - } - - if (G_LIKELY (size > 0)) { - /* only call fill when there is a size */ - ret = bclass->fill (src, offset, size, res_buf); - if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto not_ok; - } - - *buffer = res_buf; - - return GST_FLOW_OK; - - /* ERRORS */ -no_function: - { - GST_DEBUG_OBJECT (src, "no fill or alloc function"); - return GST_FLOW_NOT_SUPPORTED; - } -alloc_failed: - { - GST_DEBUG_OBJECT (src, "Failed to allocate buffer of %u bytes", size); - return ret; - } -not_ok: - { - GST_DEBUG_OBJECT (src, "fill returned %d (%s)", ret, - gst_flow_get_name (ret)); - if (*buffer == NULL) - gst_buffer_unref (res_buf); - return ret; - } -} - -/* this code implements the seeking. It is a good example - * handling all cases. - * - * A seek updates the currently configured segment.start - * and segment.stop values based on the SEEK_TYPE. If the - * segment.start value is updated, a seek to this new position - * should be performed. - * - * The seek can only be executed when we are not currently - * streaming any data, to make sure that this is the case, we - * acquire the STREAM_LOCK which is taken when we are in the - * _loop() function or when a getrange() is called. Normally - * we will not receive a seek if we are operating in pull mode - * though. When we operate as a live source we might block on the live - * cond, which does not release the STREAM_LOCK. Therefore we will try - * to grab the LIVE_LOCK instead of the STREAM_LOCK to make sure it is - * safe to perform the seek. - * - * When we are in the loop() function, we might be in the middle - * of pushing a buffer, which might block in a sink. To make sure - * that the push gets unblocked we push out a FLUSH_START event. - * Our loop function will get a FLUSHING return value from - * the push and will pause, effectively releasing the STREAM_LOCK. - * - * For a non-flushing seek, we pause the task, which might eventually - * release the STREAM_LOCK. We say eventually because when the sink - * blocks on the sample we might wait a very long time until the sink - * unblocks the sample. In any case we acquire the STREAM_LOCK and - * can continue the seek. A non-flushing seek is normally done in a - * running pipeline to perform seamless playback, this means that the sink is - * PLAYING and will return from its chain function. - * In the case of a non-flushing seek we need to make sure that the - * data we output after the seek is continuous with the previous data, - * this is because a non-flushing seek does not reset the running-time - * to 0. We do this by closing the currently running segment, ie. sending - * a new_segment event with the stop position set to the last processed - * position. - * - * After updating the segment.start/stop values, we prepare for - * streaming again. We push out a FLUSH_STOP to make the peer pad - * accept data again and we start our task again. - * - * A segment seek posts a message on the bus saying that the playback - * of the segment started. We store the segment flag internally because - * when we reach the segment.stop we have to post a segment.done - * instead of EOS when doing a segment seek. - */ -static gboolean -gst_base_src_perform_seek (GstBaseSrc * src, GstEvent * event, gboolean unlock) -{ - gboolean res = TRUE, tres; - gdouble rate; - GstFormat seek_format, dest_format; - GstSeekFlags flags; - GstSeekType start_type, stop_type; - gint64 start, stop; - gboolean flush; - gboolean update; - gboolean relative_seek = FALSE; - gboolean seekseg_configured = FALSE; - GstSegment seeksegment; - guint32 seqnum; - GstEvent *tevent; - - GST_DEBUG_OBJECT (src, "doing seek: %" GST_PTR_FORMAT, event); - - GST_OBJECT_LOCK (src); - dest_format = src->segment.format; - GST_OBJECT_UNLOCK (src); - - if (event) { - gst_event_parse_seek (event, &rate, &seek_format, &flags, - &start_type, &start, &stop_type, &stop); - - relative_seek = SEEK_TYPE_IS_RELATIVE (start_type) || - SEEK_TYPE_IS_RELATIVE (stop_type); - - if (dest_format != seek_format && !relative_seek) { - /* If we have an ABSOLUTE position (SEEK_SET only), we can convert it - * here before taking the stream lock, otherwise we must convert it later, - * once we have the stream lock and can read the last configures segment - * start and stop positions */ - gst_segment_init (&seeksegment, dest_format); - - if (!gst_base_src_prepare_seek_segment (src, event, &seeksegment)) - goto prepare_failed; - - seekseg_configured = TRUE; - } - - flush = flags & GST_SEEK_FLAG_FLUSH; - seqnum = gst_event_get_seqnum (event); - } else { - flush = FALSE; - /* get next seqnum */ - seqnum = gst_util_seqnum_next (); - } - - /* send flush start */ - if (flush) { - tevent = gst_event_new_flush_start (); - gst_event_set_seqnum (tevent, seqnum); - gst_pad_push_event (src->srcpad, tevent); - } else - gst_pad_pause_task (src->srcpad); - - /* unblock streaming thread. */ - if (unlock) - gst_base_src_set_flushing (src, TRUE); - - /* grab streaming lock, this should eventually be possible, either - * because the task is paused, our streaming thread stopped - * or because our peer is flushing. */ - GST_PAD_STREAM_LOCK (src->srcpad); - if (G_UNLIKELY (src->priv->seqnum == seqnum)) { - /* we have seen this event before, issue a warning for now */ - GST_WARNING_OBJECT (src, "duplicate event found %" G_GUINT32_FORMAT, - seqnum); - } else { - src->priv->seqnum = seqnum; - GST_DEBUG_OBJECT (src, "seek with seqnum %" G_GUINT32_FORMAT, seqnum); - } - - if (unlock) - gst_base_src_set_flushing (src, FALSE); - - /* If we configured the seeksegment above, don't overwrite it now. Otherwise - * copy the current segment info into the temp segment that we can actually - * attempt the seek with. We only update the real segment if the seek succeeds. */ - if (!seekseg_configured) { - memcpy (&seeksegment, &src->segment, sizeof (GstSegment)); - - /* now configure the final seek segment */ - if (event) { - if (seeksegment.format != seek_format) { - /* OK, here's where we give the subclass a chance to convert the relative - * seek into an absolute one in the processing format. We set up any - * absolute seek above, before taking the stream lock. */ - if (!gst_base_src_prepare_seek_segment (src, event, &seeksegment)) { - GST_DEBUG_OBJECT (src, "Preparing the seek failed after flushing. " - "Aborting seek"); - res = FALSE; - } - } else { - /* The seek format matches our processing format, no need to ask the - * the subclass to configure the segment. */ - gst_segment_do_seek (&seeksegment, rate, seek_format, flags, - start_type, start, stop_type, stop, &update); - } - } - /* Else, no seek event passed, so we're just (re)starting the - current segment. */ - } - - if (res) { - GST_DEBUG_OBJECT (src, "segment configured from %" G_GINT64_FORMAT - " to %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT, - seeksegment.start, seeksegment.stop, seeksegment.position); - - /* do the seek, segment.position contains the new position. */ - res = gst_base_src_do_seek (src, &seeksegment); - } - - /* and prepare to continue streaming */ - if (flush) { - tevent = gst_event_new_flush_stop (TRUE); - gst_event_set_seqnum (tevent, seqnum); - /* send flush stop, peer will accept data and events again. We - * are not yet providing data as we still have the STREAM_LOCK. */ - gst_pad_push_event (src->srcpad, tevent); - } - - /* The subclass must have converted the segment to the processing format - * by now */ - if (res && seeksegment.format != dest_format) { - GST_DEBUG_OBJECT (src, "Subclass failed to prepare a seek segment " - "in the correct format. Aborting seek."); - res = FALSE; - } - - /* if the seek was successful, we update our real segment and push - * out the new segment. */ - if (res) { - GST_OBJECT_LOCK (src); - memcpy (&src->segment, &seeksegment, sizeof (GstSegment)); - GST_OBJECT_UNLOCK (src); - - if (seeksegment.flags & GST_SEGMENT_FLAG_SEGMENT) { - GstMessage *message; - - message = gst_message_new_segment_start (GST_OBJECT (src), - seeksegment.format, seeksegment.position); - gst_message_set_seqnum (message, seqnum); - - gst_element_post_message (GST_ELEMENT (src), message); - } - - src->priv->segment_pending = TRUE; - src->priv->segment_seqnum = seqnum; - } - - src->priv->discont = TRUE; - src->running = TRUE; - /* and restart the task in case it got paused explicitly or by - * the FLUSH_START event we pushed out. */ - tres = gst_pad_start_task (src->srcpad, (GstTaskFunction) gst_base_src_loop, - src->srcpad, NULL); - if (res && !tres) - res = FALSE; - - /* and release the lock again so we can continue streaming */ - GST_PAD_STREAM_UNLOCK (src->srcpad); - - return res; - - /* ERROR */ -prepare_failed: - GST_DEBUG_OBJECT (src, "Preparing the seek failed before flushing. " - "Aborting seek"); - return FALSE; -} - -/* all events send to this element directly. This is mainly done from the - * application. - */ -static gboolean -gst_base_src_send_event (GstElement * element, GstEvent * event) -{ - GstBaseSrc *src; - gboolean result = FALSE; - GstBaseSrcClass *bclass; - - src = GST_BASE_SRC (element); - bclass = GST_BASE_SRC_GET_CLASS (src); - - GST_DEBUG_OBJECT (src, "handling event %p %" GST_PTR_FORMAT, event, event); - - switch (GST_EVENT_TYPE (event)) { - /* bidirectional events */ - case GST_EVENT_FLUSH_START: - GST_DEBUG_OBJECT (src, "pushing flush-start event downstream"); - - result = gst_pad_push_event (src->srcpad, event); - gst_base_src_set_flushing (src, TRUE); - event = NULL; - break; - case GST_EVENT_FLUSH_STOP: - { - gboolean start; - - GST_PAD_STREAM_LOCK (src->srcpad); - gst_base_src_set_flushing (src, FALSE); - - GST_DEBUG_OBJECT (src, "pushing flush-stop event downstream"); - result = gst_pad_push_event (src->srcpad, event); - - /* For external flush, restart the task .. */ - GST_LIVE_LOCK (src); - src->priv->segment_pending = TRUE; - - GST_OBJECT_LOCK (src->srcpad); - start = (GST_PAD_MODE (src->srcpad) == GST_PAD_MODE_PUSH); - GST_OBJECT_UNLOCK (src->srcpad); - - /* ... and for live sources, only if in playing state */ - if (src->is_live) { - if (!src->live_running) - start = FALSE; - } - - if (start) - gst_pad_start_task (src->srcpad, (GstTaskFunction) gst_base_src_loop, - src->srcpad, NULL); - - GST_LIVE_UNLOCK (src); - GST_PAD_STREAM_UNLOCK (src->srcpad); - - event = NULL; - break; - } - - /* downstream serialized events */ - case GST_EVENT_EOS: - { - gboolean push_mode; - - /* queue EOS and make sure the task or pull function performs the EOS - * actions. - * - * For push mode, This will be done in 3 steps. It is required to not - * block here as gst_element_send_event() will hold the STATE_LOCK, hence - * blocking would prevent asynchronous state change to complete. - * - * 1. We stop the streaming thread - * 2. We set the pending eos - * 3. We start the streaming thread again, so it is performed - * asynchronously. - * - * For pull mode, we simply mark the pending EOS without flushing. - */ - - GST_OBJECT_LOCK (src->srcpad); - push_mode = GST_PAD_MODE (src->srcpad) == GST_PAD_MODE_PUSH; - GST_OBJECT_UNLOCK (src->srcpad); - - if (push_mode) { - gst_base_src_set_flushing (src, TRUE); - - GST_PAD_STREAM_LOCK (src->srcpad); - gst_base_src_set_flushing (src, FALSE); - - GST_OBJECT_LOCK (src); - g_atomic_int_set (&src->priv->has_pending_eos, TRUE); - if (src->priv->pending_eos) - gst_event_unref (src->priv->pending_eos); - src->priv->pending_eos = event; - GST_OBJECT_UNLOCK (src); - - GST_DEBUG_OBJECT (src, - "EOS marked, start task for asynchronous handling"); - gst_pad_start_task (src->srcpad, (GstTaskFunction) gst_base_src_loop, - src->srcpad, NULL); - - GST_PAD_STREAM_UNLOCK (src->srcpad); - } else { - /* In pull mode, we need not to return flushing to downstream, though - * the stream lock is not kept after getrange was unblocked */ - GST_OBJECT_LOCK (src); - g_atomic_int_set (&src->priv->has_pending_eos, TRUE); - if (src->priv->pending_eos) - gst_event_unref (src->priv->pending_eos); - src->priv->pending_eos = event; - GST_OBJECT_UNLOCK (src); - - gst_base_src_set_pool_flushing (src, TRUE); - if (bclass->unlock) - bclass->unlock (src); - - GST_PAD_STREAM_LOCK (src->srcpad); - if (bclass->unlock_stop) - bclass->unlock_stop (src); - gst_base_src_set_pool_flushing (src, TRUE); - GST_PAD_STREAM_UNLOCK (src->srcpad); - } - - - event = NULL; - result = TRUE; - break; - } - case GST_EVENT_SEGMENT: - /* sending random SEGMENT downstream can break sync. */ - break; - case GST_EVENT_TAG: - case GST_EVENT_SINK_MESSAGE: - case GST_EVENT_CUSTOM_DOWNSTREAM: - case GST_EVENT_CUSTOM_BOTH: - case GST_EVENT_PROTECTION: - /* Insert TAG, CUSTOM_DOWNSTREAM, CUSTOM_BOTH, PROTECTION in the dataflow */ - GST_OBJECT_LOCK (src); - src->priv->pending_events = - g_list_append (src->priv->pending_events, event); - g_atomic_int_set (&src->priv->have_events, TRUE); - GST_OBJECT_UNLOCK (src); - event = NULL; - result = TRUE; - break; - case GST_EVENT_BUFFERSIZE: - /* does not seem to make much sense currently */ - break; - - /* upstream events */ - case GST_EVENT_QOS: - /* elements should override send_event and do something */ - break; - case GST_EVENT_SEEK: - { - gboolean started; - - GST_OBJECT_LOCK (src->srcpad); - if (GST_PAD_MODE (src->srcpad) == GST_PAD_MODE_PULL) - goto wrong_mode; - started = GST_PAD_MODE (src->srcpad) == GST_PAD_MODE_PUSH; - GST_OBJECT_UNLOCK (src->srcpad); - - if (started) { - GST_DEBUG_OBJECT (src, "performing seek"); - /* when we are running in push mode, we can execute the - * seek right now. */ - result = gst_base_src_perform_seek (src, event, TRUE); - } else { - GstEvent **event_p; - - /* else we store the event and execute the seek when we - * get activated */ - GST_OBJECT_LOCK (src); - GST_DEBUG_OBJECT (src, "queueing seek"); - event_p = &src->pending_seek; - gst_event_replace ((GstEvent **) event_p, event); - GST_OBJECT_UNLOCK (src); - /* assume the seek will work */ - result = TRUE; - } - break; - } - case GST_EVENT_NAVIGATION: - /* could make sense for elements that do something with navigation events - * but then they would need to override the send_event function */ - break; - case GST_EVENT_LATENCY: - /* does not seem to make sense currently */ - break; - - /* custom events */ - case GST_EVENT_CUSTOM_UPSTREAM: - /* override send_event if you want this */ - break; - case GST_EVENT_CUSTOM_DOWNSTREAM_OOB: - case GST_EVENT_CUSTOM_BOTH_OOB: - /* insert a random custom event into the pipeline */ - GST_DEBUG_OBJECT (src, "pushing custom OOB event downstream"); - result = gst_pad_push_event (src->srcpad, event); - /* we gave away the ref to the event in the push */ - event = NULL; - break; - default: - break; - } -done: - /* if we still have a ref to the event, unref it now */ - if (event) - gst_event_unref (event); - - return result; - - /* ERRORS */ -wrong_mode: - { - GST_DEBUG_OBJECT (src, "cannot perform seek when operating in pull mode"); - GST_OBJECT_UNLOCK (src->srcpad); - result = FALSE; - goto done; - } -} - -static gboolean -gst_base_src_seekable (GstBaseSrc * src) -{ - GstBaseSrcClass *bclass; - bclass = GST_BASE_SRC_GET_CLASS (src); - if (bclass->is_seekable) - return bclass->is_seekable (src); - else - return FALSE; -} - -static void -gst_base_src_update_qos (GstBaseSrc * src, - gdouble proportion, GstClockTimeDiff diff, GstClockTime timestamp) -{ - GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, src, - "qos: proportion: %lf, diff %" G_GINT64_FORMAT ", timestamp %" - GST_TIME_FORMAT, proportion, diff, GST_TIME_ARGS (timestamp)); - - GST_OBJECT_LOCK (src); - src->priv->proportion = proportion; - src->priv->earliest_time = timestamp + diff; - GST_OBJECT_UNLOCK (src); -} - - -static gboolean -gst_base_src_default_event (GstBaseSrc * src, GstEvent * event) -{ - gboolean result; - - GST_DEBUG_OBJECT (src, "handle event %" GST_PTR_FORMAT, event); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - /* is normally called when in push mode */ - if (!gst_base_src_seekable (src)) - goto not_seekable; - - result = gst_base_src_perform_seek (src, event, TRUE); - break; - case GST_EVENT_FLUSH_START: - /* cancel any blocking getrange, is normally called - * when in pull mode. */ - result = gst_base_src_set_flushing (src, TRUE); - break; - case GST_EVENT_FLUSH_STOP: - result = gst_base_src_set_flushing (src, FALSE); - break; - case GST_EVENT_QOS: - { - gdouble proportion; - GstClockTimeDiff diff; - GstClockTime timestamp; - - gst_event_parse_qos (event, NULL, &proportion, &diff, ×tamp); - gst_base_src_update_qos (src, proportion, diff, timestamp); - result = TRUE; - break; - } - case GST_EVENT_RECONFIGURE: - result = TRUE; - break; - case GST_EVENT_LATENCY: - result = TRUE; - break; - default: - result = FALSE; - break; - } - return result; - - /* ERRORS */ -not_seekable: - { - GST_DEBUG_OBJECT (src, "is not seekable"); - return FALSE; - } -} - -static gboolean -gst_base_src_event (GstPad * pad, GstObject * parent, GstEvent * event) -{ - GstBaseSrc *src; - GstBaseSrcClass *bclass; - gboolean result = FALSE; - - src = GST_BASE_SRC (parent); - bclass = GST_BASE_SRC_GET_CLASS (src); - - if (bclass->event) { - if (!(result = bclass->event (src, event))) - goto subclass_failed; - } - -done: - gst_event_unref (event); - - return result; - - /* ERRORS */ -subclass_failed: - { - GST_DEBUG_OBJECT (src, "subclass refused event"); - goto done; - } -} - -static void -gst_base_src_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstBaseSrc *src; - - src = GST_BASE_SRC (object); - - switch (prop_id) { - case PROP_BLOCKSIZE: - gst_base_src_set_blocksize (src, g_value_get_uint (value)); - break; - case PROP_NUM_BUFFERS: - src->num_buffers = g_value_get_int (value); - break; -#ifndef GST_REMOVE_DEPRECATED - case PROP_TYPEFIND: - src->typefind = g_value_get_boolean (value); - break; -#endif - case PROP_DO_TIMESTAMP: - gst_base_src_set_do_timestamp (src, g_value_get_boolean (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_base_src_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstBaseSrc *src; - - src = GST_BASE_SRC (object); - - switch (prop_id) { - case PROP_BLOCKSIZE: - g_value_set_uint (value, gst_base_src_get_blocksize (src)); - break; - case PROP_NUM_BUFFERS: - g_value_set_int (value, src->num_buffers); - break; -#ifndef GST_REMOVE_DEPRECATED - case PROP_TYPEFIND: - g_value_set_boolean (value, src->typefind); - break; -#endif - case PROP_DO_TIMESTAMP: - g_value_set_boolean (value, gst_base_src_get_do_timestamp (src)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/* with STREAM_LOCK and LOCK */ -static GstClockReturn -gst_base_src_wait (GstBaseSrc * basesrc, GstClock * clock, GstClockTime time) -{ - GstClockReturn ret; - GstClockID id; - - id = gst_clock_new_single_shot_id (clock, time); - - basesrc->clock_id = id; - /* release the live lock while waiting */ - GST_LIVE_UNLOCK (basesrc); - - ret = gst_clock_id_wait (id, NULL); - - GST_LIVE_LOCK (basesrc); - gst_clock_id_unref (id); - basesrc->clock_id = NULL; - - return ret; -} - -/* perform synchronisation on a buffer. - * with STREAM_LOCK. - */ -static GstClockReturn -gst_base_src_do_sync (GstBaseSrc * basesrc, GstBuffer * buffer) -{ - GstClockReturn result; - GstClockTime start, end; - GstBaseSrcClass *bclass; - GstClockTime base_time; - GstClock *clock; - GstClockTime now = GST_CLOCK_TIME_NONE, pts, dts, timestamp; - gboolean do_timestamp, first, pseudo_live, is_live; - - bclass = GST_BASE_SRC_GET_CLASS (basesrc); - - start = end = -1; - if (bclass->get_times) - bclass->get_times (basesrc, buffer, &start, &end); - - /* get buffer timestamp */ - dts = GST_BUFFER_DTS (buffer); - pts = GST_BUFFER_PTS (buffer); - - if (GST_CLOCK_TIME_IS_VALID (dts)) - timestamp = dts; - else - timestamp = pts; - - /* grab the lock to prepare for clocking and calculate the startup - * latency. */ - GST_OBJECT_LOCK (basesrc); - - is_live = basesrc->is_live; - /* if we are asked to sync against the clock we are a pseudo live element */ - pseudo_live = (start != -1 && is_live); - /* check for the first buffer */ - first = (basesrc->priv->latency == -1); - - if (timestamp != -1 && pseudo_live) { - GstClockTime latency; - - /* we have a timestamp and a sync time, latency is the diff */ - if (timestamp <= start) - latency = start - timestamp; - else - latency = 0; - - if (first) { - GST_DEBUG_OBJECT (basesrc, "pseudo_live with latency %" GST_TIME_FORMAT, - GST_TIME_ARGS (latency)); - /* first time we calculate latency, just configure */ - basesrc->priv->latency = latency; - } else { - if (basesrc->priv->latency != latency) { - /* we have a new latency, FIXME post latency message */ - basesrc->priv->latency = latency; - GST_DEBUG_OBJECT (basesrc, "latency changed to %" GST_TIME_FORMAT, - GST_TIME_ARGS (latency)); - } - } - } else if (first) { - GST_DEBUG_OBJECT (basesrc, "no latency needed, live %d, sync %d", - is_live, start != -1); - basesrc->priv->latency = 0; - } - - /* get clock, if no clock, we can't sync or do timestamps */ - if ((clock = GST_ELEMENT_CLOCK (basesrc)) == NULL) - goto no_clock; - else - gst_object_ref (clock); - - base_time = GST_ELEMENT_CAST (basesrc)->base_time; - - do_timestamp = basesrc->priv->do_timestamp; - GST_OBJECT_UNLOCK (basesrc); - - /* first buffer, calculate the timestamp offset */ - if (first) { - GstClockTime running_time; - - now = gst_clock_get_time (clock); - running_time = now - base_time; - - GST_LOG_OBJECT (basesrc, - "startup PTS: %" GST_TIME_FORMAT ", DTS %" GST_TIME_FORMAT - ", running_time %" GST_TIME_FORMAT, GST_TIME_ARGS (pts), - GST_TIME_ARGS (dts), GST_TIME_ARGS (running_time)); - - if (pseudo_live && timestamp != -1) { - /* live source and we need to sync, add startup latency to all timestamps - * to get the real running_time. Live sources should always timestamp - * according to the current running time. */ - basesrc->priv->ts_offset = GST_CLOCK_DIFF (timestamp, running_time); - - GST_LOG_OBJECT (basesrc, "live with sync, ts_offset %" GST_TIME_FORMAT, - GST_TIME_ARGS (basesrc->priv->ts_offset)); - } else { - basesrc->priv->ts_offset = 0; - GST_LOG_OBJECT (basesrc, "no timestamp offset needed"); - } - - if (!GST_CLOCK_TIME_IS_VALID (dts)) { - if (do_timestamp) { - dts = running_time; - } else if (!GST_CLOCK_TIME_IS_VALID (pts)) { - if (GST_CLOCK_TIME_IS_VALID (basesrc->segment.start)) { - dts = basesrc->segment.start; - } else { - dts = 0; - } - } - GST_BUFFER_DTS (buffer) = dts; - - GST_LOG_OBJECT (basesrc, "created DTS %" GST_TIME_FORMAT, - GST_TIME_ARGS (dts)); - } - } else { - /* not the first buffer, the timestamp is the diff between the clock and - * base_time */ - if (do_timestamp && !GST_CLOCK_TIME_IS_VALID (dts)) { - now = gst_clock_get_time (clock); - - dts = now - base_time; - GST_BUFFER_DTS (buffer) = dts; - - GST_LOG_OBJECT (basesrc, "created DTS %" GST_TIME_FORMAT, - GST_TIME_ARGS (dts)); - } - } - if (!GST_CLOCK_TIME_IS_VALID (pts)) { - if (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) - pts = dts; - - GST_BUFFER_PTS (buffer) = dts; - - GST_LOG_OBJECT (basesrc, "created PTS %" GST_TIME_FORMAT, - GST_TIME_ARGS (pts)); - } - - /* if we don't have a buffer timestamp, we don't sync */ - if (!GST_CLOCK_TIME_IS_VALID (start)) - goto no_sync; - - if (is_live) { - /* for pseudo live sources, add our ts_offset to the timestamp */ - if (GST_CLOCK_TIME_IS_VALID (pts)) - GST_BUFFER_PTS (buffer) += basesrc->priv->ts_offset; - if (GST_CLOCK_TIME_IS_VALID (dts)) - GST_BUFFER_DTS (buffer) += basesrc->priv->ts_offset; - start += basesrc->priv->ts_offset; - } - - GST_LOG_OBJECT (basesrc, - "waiting for clock, base time %" GST_TIME_FORMAT - ", stream_start %" GST_TIME_FORMAT, - GST_TIME_ARGS (base_time), GST_TIME_ARGS (start)); - - result = gst_base_src_wait (basesrc, clock, start + base_time); - - gst_object_unref (clock); - - GST_LOG_OBJECT (basesrc, "clock entry done: %d", result); - - return result; - - /* special cases */ -no_clock: - { - GST_DEBUG_OBJECT (basesrc, "we have no clock"); - GST_OBJECT_UNLOCK (basesrc); - return GST_CLOCK_OK; - } -no_sync: - { - GST_DEBUG_OBJECT (basesrc, "no sync needed"); - gst_object_unref (clock); - return GST_CLOCK_OK; - } -} - -/* Called with STREAM_LOCK and LIVE_LOCK */ -static gboolean -gst_base_src_update_length (GstBaseSrc * src, guint64 offset, guint * length, - gboolean force) -{ - guint64 size, maxsize; - GstBaseSrcClass *bclass; - gint64 stop; - - /* only operate if we are working with bytes */ - if (src->segment.format != GST_FORMAT_BYTES) - return TRUE; - - bclass = GST_BASE_SRC_GET_CLASS (src); - - stop = src->segment.stop; - /* get total file size */ - size = src->segment.duration; - - /* when not doing automatic EOS, just use the stop position. We don't use - * the size to check for EOS */ - if (!g_atomic_int_get (&src->priv->automatic_eos)) - maxsize = stop; - /* Otherwise, the max amount of bytes to read is the total - * size or up to the segment.stop if present. */ - else if (stop != -1) - maxsize = size != -1 ? MIN (size, stop) : stop; - else - maxsize = size; - - GST_DEBUG_OBJECT (src, - "reading offset %" G_GUINT64_FORMAT ", length %u, size %" G_GINT64_FORMAT - ", segment.stop %" G_GINT64_FORMAT ", maxsize %" G_GINT64_FORMAT, offset, - *length, size, stop, maxsize); - - /* check size if we have one */ - if (maxsize != -1) { - /* if we run past the end, check if the file became bigger and - * retry. Mind wrap when checking. */ - if (G_UNLIKELY (offset >= maxsize || offset + *length >= maxsize || force)) { - /* see if length of the file changed */ - if (bclass->get_size) - if (!bclass->get_size (src, &size)) - size = -1; - - /* when not doing automatic EOS, just use the stop position. We don't use - * the size to check for EOS */ - if (!g_atomic_int_get (&src->priv->automatic_eos)) - maxsize = stop; - /* Otherwise, the max amount of bytes to read is the total - * size or up to the segment.stop if present. */ - else if (stop != -1) - maxsize = size != -1 ? MIN (size, stop) : stop; - else - maxsize = size; - - if (maxsize != -1) { - /* if we are at or past the end, EOS */ - if (G_UNLIKELY (offset >= maxsize)) - goto unexpected_length; - - /* else we can clip to the end */ - if (G_UNLIKELY (offset + *length >= maxsize)) - *length = maxsize - offset; - } - } - } - - /* keep track of current duration. segment is in bytes, we checked - * that above. */ - GST_OBJECT_LOCK (src); - src->segment.duration = size; - GST_OBJECT_UNLOCK (src); - - return TRUE; - - /* ERRORS */ -unexpected_length: - { - GST_DEBUG_OBJECT (src, "processing at or past EOS"); - return FALSE; - } -} - -/* must be called with LIVE_LOCK */ -static GstFlowReturn -gst_base_src_get_range (GstBaseSrc * src, guint64 offset, guint length, - GstBuffer ** buf) -{ - GstFlowReturn ret; - GstBaseSrcClass *bclass; - GstClockReturn status; - GstBuffer *res_buf; - GstBuffer *in_buf; - gboolean own_res_buf; - - bclass = GST_BASE_SRC_GET_CLASS (src); - -again: - if (src->is_live) { - if (G_UNLIKELY (!src->live_running)) { - ret = gst_base_src_wait_playing_unlocked (src); - if (ret != GST_FLOW_OK) - goto stopped; - } - } - - if (G_UNLIKELY (!GST_BASE_SRC_IS_STARTED (src) - && !GST_BASE_SRC_IS_STARTING (src))) - goto not_started; - - if (G_UNLIKELY (!bclass->create)) - goto no_function; - - if (G_UNLIKELY (!gst_base_src_update_length (src, offset, &length, FALSE))) - goto unexpected_length; - - /* track position */ - GST_OBJECT_LOCK (src); - if (src->segment.format == GST_FORMAT_BYTES) - src->segment.position = offset; - GST_OBJECT_UNLOCK (src); - - /* normally we don't count buffers */ - if (G_UNLIKELY (src->num_buffers_left >= 0)) { - if (src->num_buffers_left == 0) - goto reached_num_buffers; - else - src->num_buffers_left--; - } - - /* don't enter the create function if a pending EOS event was set. For the - * logic of the has_pending_eos, check the event function of this class. */ - if (G_UNLIKELY (g_atomic_int_get (&src->priv->has_pending_eos))) { - src->priv->forced_eos = TRUE; - goto eos; - } - - GST_DEBUG_OBJECT (src, - "calling create offset %" G_GUINT64_FORMAT " length %u, time %" - G_GINT64_FORMAT, offset, length, src->segment.time); - - res_buf = in_buf = *buf; - own_res_buf = (*buf == NULL); - - GST_LIVE_UNLOCK (src); - ret = bclass->create (src, offset, length, &res_buf); - GST_LIVE_LOCK (src); - - /* As we released the LIVE_LOCK, the state may have changed */ - if (src->is_live) { - if (G_UNLIKELY (!src->live_running)) { - GstFlowReturn wait_ret; - wait_ret = gst_base_src_wait_playing_unlocked (src); - if (wait_ret != GST_FLOW_OK) { - if (ret == GST_FLOW_OK && own_res_buf) - gst_buffer_unref (res_buf); - ret = wait_ret; - goto stopped; - } - } - } - - /* The create function could be unlocked because we have a pending EOS. It's - * possible that we have a valid buffer from create that we need to - * discard when the create function returned _OK. */ - if (G_UNLIKELY (g_atomic_int_get (&src->priv->has_pending_eos))) { - if (ret == GST_FLOW_OK) { - if (own_res_buf) - gst_buffer_unref (res_buf); - } - src->priv->forced_eos = TRUE; - goto eos; - } - - if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto not_ok; - - /* fallback in case the create function didn't fill a provided buffer */ - if (in_buf != NULL && res_buf != in_buf) { - GstMapInfo info; - gsize copied_size; - - GST_CAT_DEBUG_OBJECT (GST_CAT_PERFORMANCE, src, "create function didn't " - "fill the provided buffer, copying"); - - if (!gst_buffer_map (in_buf, &info, GST_MAP_WRITE)) - goto map_failed; - - copied_size = gst_buffer_extract (res_buf, 0, info.data, info.size); - gst_buffer_unmap (in_buf, &info); - gst_buffer_set_size (in_buf, copied_size); - - gst_buffer_copy_into (in_buf, res_buf, GST_BUFFER_COPY_METADATA, 0, -1); - - gst_buffer_unref (res_buf); - res_buf = in_buf; - } - - if (res_buf == NULL) { - GstBufferList *pending_list = src->priv->pending_bufferlist; - - if (pending_list == NULL || gst_buffer_list_length (pending_list) == 0) - goto null_buffer; - - res_buf = gst_buffer_list_get_writable (pending_list, 0); - own_res_buf = FALSE; - } - - /* no timestamp set and we are at offset 0, we can timestamp with 0 */ - if (offset == 0 && src->segment.time == 0 - && GST_BUFFER_DTS (res_buf) == -1 && !src->is_live) { - GST_DEBUG_OBJECT (src, "setting first timestamp to 0"); - res_buf = gst_buffer_make_writable (res_buf); - GST_BUFFER_DTS (res_buf) = 0; - } - - /* now sync before pushing the buffer */ - status = gst_base_src_do_sync (src, res_buf); - - /* waiting for the clock could have made us flushing */ - if (G_UNLIKELY (src->priv->flushing)) - goto flushing; - - switch (status) { - case GST_CLOCK_EARLY: - /* the buffer is too late. We currently don't drop the buffer. */ - GST_DEBUG_OBJECT (src, "buffer too late!, returning anyway"); - break; - case GST_CLOCK_OK: - /* buffer synchronised properly */ - GST_DEBUG_OBJECT (src, "buffer ok"); - break; - case GST_CLOCK_UNSCHEDULED: - /* this case is triggered when we were waiting for the clock and - * it got unlocked because we did a state change. In any case, get rid of - * the buffer. */ - if (own_res_buf) - gst_buffer_unref (res_buf); - - if (!src->live_running) { - /* We return FLUSHING when we are not running to stop the dataflow also - * get rid of the produced buffer. */ - GST_DEBUG_OBJECT (src, - "clock was unscheduled (%d), returning FLUSHING", status); - ret = GST_FLOW_FLUSHING; - } else { - /* If we are running when this happens, we quickly switched between - * pause and playing. We try to produce a new buffer */ - GST_DEBUG_OBJECT (src, - "clock was unscheduled (%d), but we are running", status); - goto again; - } - break; - default: - /* all other result values are unexpected and errors */ - GST_ELEMENT_ERROR (src, CORE, CLOCK, - (_("Internal clock error.")), - ("clock returned unexpected return value %d", status)); - if (own_res_buf) - gst_buffer_unref (res_buf); - ret = GST_FLOW_ERROR; - break; - } - if (G_LIKELY (ret == GST_FLOW_OK)) - *buf = res_buf; - - return ret; - - /* ERROR */ -stopped: - { - GST_DEBUG_OBJECT (src, "wait_playing returned %d (%s)", ret, - gst_flow_get_name (ret)); - return ret; - } -not_ok: - { - GST_DEBUG_OBJECT (src, "create returned %d (%s)", ret, - gst_flow_get_name (ret)); - return ret; - } -map_failed: - { - GST_ELEMENT_ERROR (src, RESOURCE, BUSY, - (_("Failed to map buffer.")), - ("failed to map result buffer in WRITE mode")); - if (own_res_buf) - gst_buffer_unref (res_buf); - return GST_FLOW_ERROR; - } -not_started: - { - GST_DEBUG_OBJECT (src, "getrange but not started"); - return GST_FLOW_FLUSHING; - } -no_function: - { - GST_DEBUG_OBJECT (src, "no create function"); - return GST_FLOW_NOT_SUPPORTED; - } -unexpected_length: - { - GST_DEBUG_OBJECT (src, "unexpected length %u (offset=%" G_GUINT64_FORMAT - ", size=%" G_GINT64_FORMAT ")", length, offset, src->segment.duration); - return GST_FLOW_EOS; - } -reached_num_buffers: - { - GST_DEBUG_OBJECT (src, "sent all buffers"); - return GST_FLOW_EOS; - } -flushing: - { - GST_DEBUG_OBJECT (src, "we are flushing"); - if (own_res_buf) - gst_buffer_unref (res_buf); - return GST_FLOW_FLUSHING; - } -eos: - { - GST_DEBUG_OBJECT (src, "we are EOS"); - return GST_FLOW_EOS; - } -null_buffer: - { - GST_ELEMENT_ERROR (src, STREAM, FAILED, - (_("Internal data flow error.")), - ("Subclass %s neither returned a buffer nor submitted a buffer list " - "from its create function", G_OBJECT_TYPE_NAME (src))); - return GST_FLOW_ERROR; - } -} - -static GstFlowReturn -gst_base_src_getrange (GstPad * pad, GstObject * parent, guint64 offset, - guint length, GstBuffer ** buf) -{ - GstBaseSrc *src; - GstFlowReturn res; - - src = GST_BASE_SRC_CAST (parent); - - GST_LIVE_LOCK (src); - if (G_UNLIKELY (src->priv->flushing)) - goto flushing; - - res = gst_base_src_get_range (src, offset, length, buf); - -done: - GST_LIVE_UNLOCK (src); - - return res; - - /* ERRORS */ -flushing: - { - GST_DEBUG_OBJECT (src, "we are flushing"); - res = GST_FLOW_FLUSHING; - goto done; - } -} - -static gboolean -gst_base_src_is_random_access (GstBaseSrc * src) -{ - /* we need to start the basesrc to check random access */ - if (!GST_BASE_SRC_IS_STARTED (src)) { - GST_LOG_OBJECT (src, "doing start/stop to check get_range support"); - if (G_LIKELY (gst_base_src_start (src))) { - if (gst_base_src_start_wait (src) != GST_FLOW_OK) - goto start_failed; - gst_base_src_stop (src); - } - } - - return src->random_access; - - /* ERRORS */ -start_failed: - { - GST_DEBUG_OBJECT (src, "failed to start"); - return FALSE; - } -} - -/* Called with STREAM_LOCK */ -static void -gst_base_src_loop (GstPad * pad) -{ - GstBaseSrc *src; - GstBuffer *buf = NULL; - GstFlowReturn ret; - gint64 position; - gboolean eos; - guint blocksize; - GList *pending_events = NULL, *tmp; - - eos = FALSE; - - src = GST_BASE_SRC (GST_OBJECT_PARENT (pad)); - - /* Just leave immediately if we're flushing */ - GST_LIVE_LOCK (src); - if (G_UNLIKELY (src->priv->flushing || GST_PAD_IS_FLUSHING (pad))) - goto flushing; - GST_LIVE_UNLOCK (src); - - /* Just return if EOS is pushed again, as the app might be unaware that an - * EOS have been sent already */ - if (GST_PAD_IS_EOS (pad)) { - GST_DEBUG_OBJECT (src, "Pad is marked as EOS, pause the task"); - gst_pad_pause_task (pad); - goto done; - } - - gst_base_src_send_stream_start (src); - - /* The stream-start event could've caused something to flush us */ - GST_LIVE_LOCK (src); - if (G_UNLIKELY (src->priv->flushing || GST_PAD_IS_FLUSHING (pad))) - goto flushing; - GST_LIVE_UNLOCK (src); - - /* check if we need to renegotiate */ - if (gst_pad_check_reconfigure (pad)) { - if (!gst_base_src_negotiate_unlocked (src)) { - gst_pad_mark_reconfigure (pad); - if (GST_PAD_IS_FLUSHING (pad)) { - GST_LIVE_LOCK (src); - goto flushing; - } else { - goto negotiate_failed; - } - } - } - - GST_LIVE_LOCK (src); - - if (G_UNLIKELY (src->priv->flushing || GST_PAD_IS_FLUSHING (pad))) - goto flushing; - - blocksize = src->blocksize; - - /* if we operate in bytes, we can calculate an offset */ - if (src->segment.format == GST_FORMAT_BYTES) { - position = src->segment.position; - /* for negative rates, start with subtracting the blocksize */ - if (src->segment.rate < 0.0) { - /* we cannot go below segment.start */ - if (position > src->segment.start + blocksize) - position -= blocksize; - else { - /* last block, remainder up to segment.start */ - blocksize = position - src->segment.start; - position = src->segment.start; - } - } - } else - position = -1; - - GST_LOG_OBJECT (src, "next_ts %" GST_TIME_FORMAT " size %u", - GST_TIME_ARGS (position), blocksize); - - /* clean up just in case we got interrupted or so last time round */ - if (src->priv->pending_bufferlist != NULL) { - gst_buffer_list_unref (src->priv->pending_bufferlist); - src->priv->pending_bufferlist = NULL; - } - - ret = gst_base_src_get_range (src, position, blocksize, &buf); - if (G_UNLIKELY (ret != GST_FLOW_OK)) { - GST_INFO_OBJECT (src, "pausing after gst_base_src_get_range() = %s", - gst_flow_get_name (ret)); - GST_LIVE_UNLOCK (src); - goto pause; - } - - /* Note: at this point buf might be a single buf returned which we own or - * the first buf of a pending buffer list submitted via submit_buffer_list(), - * in which case the buffer is owned by the pending buffer list and not us. */ - g_assert (buf != NULL); - - /* push events to close/start our segment before we push the buffer. */ - if (G_UNLIKELY (src->priv->segment_pending)) { - GstEvent *seg_event = gst_event_new_segment (&src->segment); - - gst_event_set_seqnum (seg_event, src->priv->segment_seqnum); - src->priv->segment_seqnum = gst_util_seqnum_next (); - gst_pad_push_event (pad, seg_event); - src->priv->segment_pending = FALSE; - } - - if (g_atomic_int_get (&src->priv->have_events)) { - GST_OBJECT_LOCK (src); - /* take the events */ - pending_events = src->priv->pending_events; - src->priv->pending_events = NULL; - g_atomic_int_set (&src->priv->have_events, FALSE); - GST_OBJECT_UNLOCK (src); - } - - /* Push out pending events if any */ - if (G_UNLIKELY (pending_events != NULL)) { - for (tmp = pending_events; tmp; tmp = g_list_next (tmp)) { - GstEvent *ev = (GstEvent *) tmp->data; - gst_pad_push_event (pad, ev); - } - g_list_free (pending_events); - } - - /* figure out the new position */ - switch (src->segment.format) { - case GST_FORMAT_BYTES: - { - guint bufsize = gst_buffer_get_size (buf); - - /* we subtracted above for negative rates */ - if (src->segment.rate >= 0.0) - position += bufsize; - break; - } - case GST_FORMAT_TIME: - { - GstClockTime start, duration; - - start = GST_BUFFER_TIMESTAMP (buf); - duration = GST_BUFFER_DURATION (buf); - - if (GST_CLOCK_TIME_IS_VALID (start)) - position = start; - else - position = src->segment.position; - - if (GST_CLOCK_TIME_IS_VALID (duration)) { - if (src->segment.rate >= 0.0) - position += duration; - } - break; - } - case GST_FORMAT_DEFAULT: - if (src->segment.rate >= 0.0) - position = GST_BUFFER_OFFSET_END (buf); - else - position = GST_BUFFER_OFFSET (buf); - break; - default: - position = -1; - break; - } - if (position != -1) { - if (src->segment.rate >= 0.0) { - /* positive rate, check if we reached the stop */ - if (src->segment.stop != -1) { - if (position >= src->segment.stop) { - if (g_atomic_int_get (&src->priv->automatic_eos)) - eos = TRUE; - position = src->segment.stop; - } - } - } else { - /* negative rate, check if we reached the start. start is always set to - * something different from -1 */ - if (position <= src->segment.start) { - if (g_atomic_int_get (&src->priv->automatic_eos)) - eos = TRUE; - position = src->segment.start; - } - /* when going reverse, all buffers are DISCONT */ - src->priv->discont = TRUE; - } - GST_OBJECT_LOCK (src); - src->segment.position = position; - GST_OBJECT_UNLOCK (src); - } - - if (G_UNLIKELY (src->priv->discont)) { - GST_INFO_OBJECT (src, "marking pending DISCONT"); - buf = gst_buffer_make_writable (buf); - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); - src->priv->discont = FALSE; - } - GST_LIVE_UNLOCK (src); - - /* push buffer or buffer list */ - if (src->priv->pending_bufferlist != NULL) { - ret = gst_pad_push_list (pad, src->priv->pending_bufferlist); - src->priv->pending_bufferlist = NULL; - } else { - ret = gst_pad_push (pad, buf); - } - - if (G_UNLIKELY (ret != GST_FLOW_OK)) { - if (ret == GST_FLOW_NOT_NEGOTIATED) { - goto not_negotiated; - } - GST_INFO_OBJECT (src, "pausing after gst_pad_push() = %s", - gst_flow_get_name (ret)); - goto pause; - } - - /* Segment pending means that a new segment was configured - * during this loop run */ - if (G_UNLIKELY (eos && !src->priv->segment_pending)) { - GST_INFO_OBJECT (src, "pausing after end of segment"); - ret = GST_FLOW_EOS; - goto pause; - } - -done: - return; - - /* special cases */ -not_negotiated: - { - if (gst_pad_needs_reconfigure (pad)) { - GST_DEBUG_OBJECT (src, "Retrying to renegotiate"); - return; - } - /* fallthrough when push returns NOT_NEGOTIATED and we don't have - * a pending negotiation request on our srcpad */ - } -negotiate_failed: - { - GST_DEBUG_OBJECT (src, "Not negotiated"); - ret = GST_FLOW_NOT_NEGOTIATED; - goto pause; - } -flushing: - { - GST_DEBUG_OBJECT (src, "we are flushing"); - GST_LIVE_UNLOCK (src); - ret = GST_FLOW_FLUSHING; - goto pause; - } -pause: - { - GstEvent *event; - - GST_DEBUG_OBJECT (src, "pausing task, reason %s", gst_flow_get_name (ret)); - src->running = FALSE; - gst_pad_pause_task (pad); - if (ret == GST_FLOW_EOS) { - gboolean flag_segment; - GstFormat format; - gint64 position; - - flag_segment = (src->segment.flags & GST_SEGMENT_FLAG_SEGMENT) != 0; - format = src->segment.format; - position = src->segment.position; - - /* perform EOS logic */ - if (src->priv->forced_eos) { - g_assert (g_atomic_int_get (&src->priv->has_pending_eos)); - GST_OBJECT_LOCK (src); - event = src->priv->pending_eos; - src->priv->pending_eos = NULL; - GST_OBJECT_UNLOCK (src); - - } else if (flag_segment) { - GstMessage *message; - - message = gst_message_new_segment_done (GST_OBJECT_CAST (src), - format, position); - gst_message_set_seqnum (message, src->priv->seqnum); - gst_element_post_message (GST_ELEMENT_CAST (src), message); - event = gst_event_new_segment_done (format, position); - gst_event_set_seqnum (event, src->priv->seqnum); - - } else { - event = gst_event_new_eos (); - gst_event_set_seqnum (event, src->priv->seqnum); - } - - gst_pad_push_event (pad, event); - src->priv->forced_eos = FALSE; - - } else if (ret == GST_FLOW_NOT_LINKED || ret <= GST_FLOW_EOS) { - event = gst_event_new_eos (); - gst_event_set_seqnum (event, src->priv->seqnum); - /* for fatal errors we post an error message, post the error - * first so the app knows about the error first. - * Also don't do this for FLUSHING because it happens - * due to flushing and posting an error message because of - * that is the wrong thing to do, e.g. when we're doing - * a flushing seek. */ - GST_ELEMENT_FLOW_ERROR (src, ret); - gst_pad_push_event (pad, event); - } - goto done; - } -} - -static gboolean -gst_base_src_set_allocation (GstBaseSrc * basesrc, GstBufferPool * pool, - GstAllocator * allocator, const GstAllocationParams * params) -{ - GstAllocator *oldalloc; - GstBufferPool *oldpool; - GstBaseSrcPrivate *priv = basesrc->priv; - - if (pool) { - GST_DEBUG_OBJECT (basesrc, "activate pool"); - if (!gst_buffer_pool_set_active (pool, TRUE)) - goto activate_failed; - } - - GST_OBJECT_LOCK (basesrc); - oldpool = priv->pool; - priv->pool = pool; - - oldalloc = priv->allocator; - priv->allocator = allocator; - - if (priv->pool) - gst_object_ref (priv->pool); - if (priv->allocator) - gst_object_ref (priv->allocator); - - if (params) - priv->params = *params; - else - gst_allocation_params_init (&priv->params); - GST_OBJECT_UNLOCK (basesrc); - - if (oldpool) { - /* only deactivate if the pool is not the one we're using */ - if (oldpool != pool) { - GST_DEBUG_OBJECT (basesrc, "deactivate old pool"); - gst_buffer_pool_set_active (oldpool, FALSE); - } - gst_object_unref (oldpool); - } - if (oldalloc) { - gst_object_unref (oldalloc); - } - return TRUE; - - /* ERRORS */ -activate_failed: - { - GST_ERROR_OBJECT (basesrc, "failed to activate bufferpool."); - return FALSE; - } -} - -static void -gst_base_src_set_pool_flushing (GstBaseSrc * basesrc, gboolean flushing) -{ - GstBaseSrcPrivate *priv = basesrc->priv; - GstBufferPool *pool; - - GST_OBJECT_LOCK (basesrc); - if ((pool = priv->pool)) - pool = gst_object_ref (pool); - GST_OBJECT_UNLOCK (basesrc); - - if (pool) { - gst_buffer_pool_set_flushing (pool, flushing); - gst_object_unref (pool); - } -} - - -static gboolean -gst_base_src_decide_allocation_default (GstBaseSrc * basesrc, GstQuery * query) -{ - GstCaps *outcaps; - GstBufferPool *pool; - guint size, min, max; - GstAllocator *allocator; - GstAllocationParams params; - GstStructure *config; - gboolean update_allocator; - - gst_query_parse_allocation (query, &outcaps, NULL); - - /* we got configuration from our peer or the decide_allocation method, - * parse them */ - if (gst_query_get_n_allocation_params (query) > 0) { - /* try the allocator */ - gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶ms); - update_allocator = TRUE; - } else { - allocator = NULL; - gst_allocation_params_init (¶ms); - update_allocator = FALSE; - } - - if (gst_query_get_n_allocation_pools (query) > 0) { - gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); - - if (pool == NULL) { - /* no pool, we can make our own */ - GST_DEBUG_OBJECT (basesrc, "no pool, making new pool"); - pool = gst_buffer_pool_new (); - } - } else { - pool = NULL; - size = min = max = 0; - } - - /* now configure */ - if (pool) { - config = gst_buffer_pool_get_config (pool); - gst_buffer_pool_config_set_params (config, outcaps, size, min, max); - gst_buffer_pool_config_set_allocator (config, allocator, ¶ms); - - /* buffer pool may have to do some changes */ - if (!gst_buffer_pool_set_config (pool, config)) { - config = gst_buffer_pool_get_config (pool); - - /* If change are not acceptable, fallback to generic pool */ - if (!gst_buffer_pool_config_validate_params (config, outcaps, size, min, - max)) { - GST_DEBUG_OBJECT (basesrc, "unsupported pool, making new pool"); - - gst_object_unref (pool); - pool = gst_buffer_pool_new (); - gst_buffer_pool_config_set_params (config, outcaps, size, min, max); - gst_buffer_pool_config_set_allocator (config, allocator, ¶ms); - } - - if (!gst_buffer_pool_set_config (pool, config)) - goto config_failed; - } - } - - if (update_allocator) - gst_query_set_nth_allocation_param (query, 0, allocator, ¶ms); - else - gst_query_add_allocation_param (query, allocator, ¶ms); - if (allocator) - gst_object_unref (allocator); - - if (pool) { - gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max); - gst_object_unref (pool); - } - - return TRUE; - -config_failed: - GST_ELEMENT_ERROR (basesrc, RESOURCE, SETTINGS, - ("Failed to configure the buffer pool"), - ("Configuration is most likely invalid, please report this issue.")); - gst_object_unref (pool); - return FALSE; -} - -static gboolean -gst_base_src_prepare_allocation (GstBaseSrc * basesrc, GstCaps * caps) -{ - GstBaseSrcClass *bclass; - gboolean result = TRUE; - GstQuery *query; - GstBufferPool *pool = NULL; - GstAllocator *allocator = NULL; - GstAllocationParams params; - - bclass = GST_BASE_SRC_GET_CLASS (basesrc); - - /* make query and let peer pad answer, we don't really care if it worked or - * not, if it failed, the allocation query would contain defaults and the - * subclass would then set better values if needed */ - query = gst_query_new_allocation (caps, TRUE); - if (!gst_pad_peer_query (basesrc->srcpad, query)) { - /* not a problem, just debug a little */ - GST_DEBUG_OBJECT (basesrc, "peer ALLOCATION query failed"); - } - - g_assert (bclass->decide_allocation != NULL); - result = bclass->decide_allocation (basesrc, query); - - GST_DEBUG_OBJECT (basesrc, "ALLOCATION (%d) params: %" GST_PTR_FORMAT, result, - query); - - if (!result) - goto no_decide_allocation; - - /* we got configuration from our peer or the decide_allocation method, - * parse them */ - if (gst_query_get_n_allocation_params (query) > 0) { - gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶ms); - } else { - allocator = NULL; - gst_allocation_params_init (¶ms); - } - - if (gst_query_get_n_allocation_pools (query) > 0) - gst_query_parse_nth_allocation_pool (query, 0, &pool, NULL, NULL, NULL); - - result = gst_base_src_set_allocation (basesrc, pool, allocator, ¶ms); - - if (allocator) - gst_object_unref (allocator); - if (pool) - gst_object_unref (pool); - - gst_query_unref (query); - - return result; - - /* Errors */ -no_decide_allocation: - { - GST_WARNING_OBJECT (basesrc, "Subclass failed to decide allocation"); - gst_query_unref (query); - - return result; - } -} - -/* default negotiation code. - * - * Take intersection between src and sink pads, take first - * caps and fixate. - */ -static gboolean -gst_base_src_default_negotiate (GstBaseSrc * basesrc) -{ - GstCaps *thiscaps; - GstCaps *caps = NULL; - GstCaps *peercaps = NULL; - gboolean result = FALSE; - - /* first see what is possible on our source pad */ - thiscaps = gst_pad_query_caps (GST_BASE_SRC_PAD (basesrc), NULL); - GST_DEBUG_OBJECT (basesrc, "caps of src: %" GST_PTR_FORMAT, thiscaps); - /* nothing or anything is allowed, we're done */ - if (thiscaps == NULL || gst_caps_is_any (thiscaps)) - goto no_nego_needed; - - if (G_UNLIKELY (gst_caps_is_empty (thiscaps))) - goto no_caps; - - /* get the peer caps */ - peercaps = gst_pad_peer_query_caps (GST_BASE_SRC_PAD (basesrc), thiscaps); - GST_DEBUG_OBJECT (basesrc, "caps of peer: %" GST_PTR_FORMAT, peercaps); - if (peercaps) { - /* The result is already a subset of our caps */ - caps = peercaps; - gst_caps_unref (thiscaps); - } else { - /* no peer, work with our own caps then */ - caps = thiscaps; - } - if (caps && !gst_caps_is_empty (caps)) { - /* now fixate */ - GST_DEBUG_OBJECT (basesrc, "have caps: %" GST_PTR_FORMAT, caps); - if (gst_caps_is_any (caps)) { - GST_DEBUG_OBJECT (basesrc, "any caps, we stop"); - /* hmm, still anything, so element can do anything and - * nego is not needed */ - result = TRUE; - } else { - caps = gst_base_src_fixate (basesrc, caps); - GST_DEBUG_OBJECT (basesrc, "fixated to: %" GST_PTR_FORMAT, caps); - if (gst_caps_is_fixed (caps)) { - /* yay, fixed caps, use those then, it's possible that the subclass does - * not accept this caps after all and we have to fail. */ - result = gst_base_src_set_caps (basesrc, caps); - } - } - gst_caps_unref (caps); - } else { - if (caps) - gst_caps_unref (caps); - GST_DEBUG_OBJECT (basesrc, "no common caps"); - } - return result; - -no_nego_needed: - { - GST_DEBUG_OBJECT (basesrc, "no negotiation needed"); - if (thiscaps) - gst_caps_unref (thiscaps); - return TRUE; - } -no_caps: - { - GST_ELEMENT_ERROR (basesrc, STREAM, FORMAT, - ("No supported formats found"), - ("This element did not produce valid caps")); - if (thiscaps) - gst_caps_unref (thiscaps); - return TRUE; - } -} - -static gboolean -gst_base_src_negotiate_unlocked (GstBaseSrc * basesrc) -{ - GstBaseSrcClass *bclass; - gboolean result; - - bclass = GST_BASE_SRC_GET_CLASS (basesrc); - - GST_DEBUG_OBJECT (basesrc, "starting negotiation"); - - if (G_LIKELY (bclass->negotiate)) - result = bclass->negotiate (basesrc); - else - result = TRUE; - - if (G_LIKELY (result)) { - GstCaps *caps; - - caps = gst_pad_get_current_caps (basesrc->srcpad); - - result = gst_base_src_prepare_allocation (basesrc, caps); - - if (caps) - gst_caps_unref (caps); - } - return result; -} - -/** - * gst_base_src_negotiate: - * @src: base source instance - * - * Negotiates src pad caps with downstream elements. - * Unmarks GST_PAD_FLAG_NEED_RECONFIGURE in any case. But marks it again - * if #GstBaseSrcClass::negotiate fails. - * - * Do not call this in the #GstBaseSrcClass::fill vmethod. Call this in - * #GstBaseSrcClass::create or in #GstBaseSrcClass::alloc, _before_ any - * buffer is allocated. - * - * Returns: %TRUE if the negotiation succeeded, else %FALSE. - * - * Since: 1.18 - */ -gboolean -gst_base_src_negotiate (GstBaseSrc * src) -{ - gboolean ret = TRUE; - - g_return_val_if_fail (GST_IS_BASE_SRC (src), FALSE); - - GST_PAD_STREAM_LOCK (src->srcpad); - gst_pad_check_reconfigure (src->srcpad); - ret = gst_base_src_negotiate_unlocked (src); - if (!ret) - gst_pad_mark_reconfigure (src->srcpad); - GST_PAD_STREAM_UNLOCK (src->srcpad); - - return ret; -} - -static gboolean -gst_base_src_start (GstBaseSrc * basesrc) -{ - GstBaseSrcClass *bclass; - gboolean result; - - GST_LIVE_LOCK (basesrc); - - GST_OBJECT_LOCK (basesrc); - if (GST_BASE_SRC_IS_STARTING (basesrc)) - goto was_starting; - if (GST_BASE_SRC_IS_STARTED (basesrc)) - goto was_started; - - basesrc->priv->start_result = GST_FLOW_FLUSHING; - GST_OBJECT_FLAG_SET (basesrc, GST_BASE_SRC_FLAG_STARTING); - gst_segment_init (&basesrc->segment, basesrc->segment.format); - GST_OBJECT_UNLOCK (basesrc); - - basesrc->num_buffers_left = basesrc->num_buffers; - basesrc->running = FALSE; - basesrc->priv->segment_pending = FALSE; - basesrc->priv->segment_seqnum = gst_util_seqnum_next (); - basesrc->priv->forced_eos = FALSE; - GST_LIVE_UNLOCK (basesrc); - - bclass = GST_BASE_SRC_GET_CLASS (basesrc); - if (bclass->start) - result = bclass->start (basesrc); - else - result = TRUE; - - if (!result) - goto could_not_start; - - if (!gst_base_src_is_async (basesrc)) { - gst_base_src_start_complete (basesrc, GST_FLOW_OK); - /* not really waiting here, we call this to get the result - * from the start_complete call */ - result = gst_base_src_start_wait (basesrc) == GST_FLOW_OK; - } - - return result; - - /* ERROR */ -was_starting: - { - GST_DEBUG_OBJECT (basesrc, "was starting"); - GST_OBJECT_UNLOCK (basesrc); - GST_LIVE_UNLOCK (basesrc); - return TRUE; - } -was_started: - { - GST_DEBUG_OBJECT (basesrc, "was started"); - GST_OBJECT_UNLOCK (basesrc); - GST_LIVE_UNLOCK (basesrc); - return TRUE; - } -could_not_start: - { - GST_DEBUG_OBJECT (basesrc, "could not start"); - /* subclass is supposed to post a message but we post one as a fallback - * just in case. We don't have to call _stop. */ - GST_ELEMENT_ERROR (basesrc, CORE, STATE_CHANGE, (NULL), - ("Failed to start")); - gst_base_src_start_complete (basesrc, GST_FLOW_ERROR); - return FALSE; - } -} - -/** - * gst_base_src_start_complete: - * @basesrc: base source instance - * @ret: a #GstFlowReturn - * - * Complete an asynchronous start operation. When the subclass overrides the - * start method, it should call gst_base_src_start_complete() when the start - * operation completes either from the same thread or from an asynchronous - * helper thread. - */ -void -gst_base_src_start_complete (GstBaseSrc * basesrc, GstFlowReturn ret) -{ - gboolean have_size; - guint64 size; - gboolean seekable; - GstFormat format; - GstPadMode mode; - GstEvent *event; - - if (ret != GST_FLOW_OK) - goto error; - - GST_DEBUG_OBJECT (basesrc, "starting source"); - format = basesrc->segment.format; - - /* figure out the size */ - have_size = FALSE; - size = -1; - if (format == GST_FORMAT_BYTES) { - GstBaseSrcClass *bclass = GST_BASE_SRC_GET_CLASS (basesrc); - - if (bclass->get_size) { - if (!(have_size = bclass->get_size (basesrc, &size))) - size = -1; - } - GST_DEBUG_OBJECT (basesrc, "setting size %" G_GUINT64_FORMAT, size); - /* only update the size when operating in bytes, subclass is supposed - * to set duration in the start method for other formats */ - GST_OBJECT_LOCK (basesrc); - basesrc->segment.duration = size; - GST_OBJECT_UNLOCK (basesrc); - } - - GST_DEBUG_OBJECT (basesrc, - "format: %s, have size: %d, size: %" G_GUINT64_FORMAT ", duration: %" - G_GINT64_FORMAT, gst_format_get_name (format), have_size, size, - basesrc->segment.duration); - - seekable = gst_base_src_seekable (basesrc); - GST_DEBUG_OBJECT (basesrc, "is seekable: %d", seekable); - - /* update for random access flag */ - basesrc->random_access = seekable && format == GST_FORMAT_BYTES; - - GST_DEBUG_OBJECT (basesrc, "is random_access: %d", basesrc->random_access); - - gst_pad_mark_reconfigure (GST_BASE_SRC_PAD (basesrc)); - - GST_OBJECT_LOCK (basesrc->srcpad); - mode = GST_PAD_MODE (basesrc->srcpad); - GST_OBJECT_UNLOCK (basesrc->srcpad); - - /* take the stream lock here, we only want to let the task run when we have - * set the STARTED flag */ - GST_PAD_STREAM_LOCK (basesrc->srcpad); - switch (mode) { - case GST_PAD_MODE_PUSH: - /* do initial seek, which will start the task */ - GST_OBJECT_LOCK (basesrc); - event = basesrc->pending_seek; - basesrc->pending_seek = NULL; - GST_OBJECT_UNLOCK (basesrc); - - /* The perform seek code will start the task when finished. We don't have to - * unlock the streaming thread because it is not running yet */ - if (G_UNLIKELY (!gst_base_src_perform_seek (basesrc, event, FALSE))) - goto seek_failed; - - if (event) - gst_event_unref (event); - break; - case GST_PAD_MODE_PULL: - /* if not random_access, we cannot operate in pull mode for now */ - if (G_UNLIKELY (!basesrc->random_access)) - goto no_get_range; - break; - default: - goto not_activated_yet; - break; - } - - GST_OBJECT_LOCK (basesrc); - GST_OBJECT_FLAG_SET (basesrc, GST_BASE_SRC_FLAG_STARTED); - GST_OBJECT_FLAG_UNSET (basesrc, GST_BASE_SRC_FLAG_STARTING); - basesrc->priv->start_result = ret; - GST_ASYNC_SIGNAL (basesrc); - GST_OBJECT_UNLOCK (basesrc); - - GST_PAD_STREAM_UNLOCK (basesrc->srcpad); - - return; - -seek_failed: - { - GST_PAD_STREAM_UNLOCK (basesrc->srcpad); - GST_ERROR_OBJECT (basesrc, "Failed to perform initial seek"); - gst_base_src_stop (basesrc); - if (event) - gst_event_unref (event); - ret = GST_FLOW_ERROR; - goto error; - } -no_get_range: - { - GST_PAD_STREAM_UNLOCK (basesrc->srcpad); - gst_base_src_stop (basesrc); - GST_ERROR_OBJECT (basesrc, "Cannot operate in pull mode, stopping"); - ret = GST_FLOW_ERROR; - goto error; - } -not_activated_yet: - { - GST_PAD_STREAM_UNLOCK (basesrc->srcpad); - gst_base_src_stop (basesrc); - GST_WARNING_OBJECT (basesrc, "pad not activated yet"); - ret = GST_FLOW_ERROR; - goto error; - } -error: - { - GST_OBJECT_LOCK (basesrc); - basesrc->priv->start_result = ret; - GST_OBJECT_FLAG_UNSET (basesrc, GST_BASE_SRC_FLAG_STARTING); - GST_ASYNC_SIGNAL (basesrc); - GST_OBJECT_UNLOCK (basesrc); - return; - } -} - -/** - * gst_base_src_start_wait: - * @basesrc: base source instance - * - * Wait until the start operation completes. - * - * Returns: a #GstFlowReturn. - */ -GstFlowReturn -gst_base_src_start_wait (GstBaseSrc * basesrc) -{ - GstFlowReturn result; - - GST_OBJECT_LOCK (basesrc); - while (GST_BASE_SRC_IS_STARTING (basesrc)) { - GST_ASYNC_WAIT (basesrc); - } - result = basesrc->priv->start_result; - GST_OBJECT_UNLOCK (basesrc); - - GST_DEBUG_OBJECT (basesrc, "got %s", gst_flow_get_name (result)); - - return result; -} - -static gboolean -gst_base_src_stop (GstBaseSrc * basesrc) -{ - GstBaseSrcClass *bclass; - gboolean result = TRUE; - - GST_DEBUG_OBJECT (basesrc, "stopping source"); - - /* flush all */ - gst_base_src_set_flushing (basesrc, TRUE); - - /* stop the task */ - gst_pad_stop_task (basesrc->srcpad); - /* stop flushing, this will balance unlock/unlock_stop calls */ - gst_base_src_set_flushing (basesrc, FALSE); - - GST_OBJECT_LOCK (basesrc); - if (!GST_BASE_SRC_IS_STARTED (basesrc) && !GST_BASE_SRC_IS_STARTING (basesrc)) - goto was_stopped; - - GST_OBJECT_FLAG_UNSET (basesrc, GST_BASE_SRC_FLAG_STARTING); - GST_OBJECT_FLAG_UNSET (basesrc, GST_BASE_SRC_FLAG_STARTED); - basesrc->priv->start_result = GST_FLOW_FLUSHING; - GST_ASYNC_SIGNAL (basesrc); - GST_OBJECT_UNLOCK (basesrc); - - bclass = GST_BASE_SRC_GET_CLASS (basesrc); - if (bclass->stop) - result = bclass->stop (basesrc); - - if (basesrc->priv->pending_bufferlist != NULL) { - gst_buffer_list_unref (basesrc->priv->pending_bufferlist); - basesrc->priv->pending_bufferlist = NULL; - } - - gst_base_src_set_allocation (basesrc, NULL, NULL, NULL); - - return result; - -was_stopped: - { - GST_DEBUG_OBJECT (basesrc, "was stopped"); - GST_OBJECT_UNLOCK (basesrc); - return TRUE; - } -} - -/* start or stop flushing dataprocessing - */ -static gboolean -gst_base_src_set_flushing (GstBaseSrc * basesrc, gboolean flushing) -{ - GstBaseSrcClass *bclass; - - bclass = GST_BASE_SRC_GET_CLASS (basesrc); - - GST_DEBUG_OBJECT (basesrc, "flushing %d", flushing); - - if (flushing) { - gst_base_src_set_pool_flushing (basesrc, TRUE); - /* unlock any subclasses to allow turning off the streaming thread */ - if (bclass->unlock) - bclass->unlock (basesrc); - } - - /* the live lock is released when we are blocked, waiting for playing, - * when we sync to the clock or creating a buffer */ - GST_LIVE_LOCK (basesrc); - basesrc->priv->flushing = flushing; - if (flushing) { - /* clear pending EOS if any */ - if (g_atomic_int_get (&basesrc->priv->has_pending_eos)) { - GST_OBJECT_LOCK (basesrc); - CLEAR_PENDING_EOS (basesrc); - basesrc->priv->forced_eos = FALSE; - GST_OBJECT_UNLOCK (basesrc); - } - - /* unblock clock sync (if any) or any other blocking thing */ - if (basesrc->clock_id) - gst_clock_id_unschedule (basesrc->clock_id); - } else { - gst_base_src_set_pool_flushing (basesrc, FALSE); - - /* Drop all delayed events */ - GST_OBJECT_LOCK (basesrc); - if (basesrc->priv->pending_events) { - g_list_foreach (basesrc->priv->pending_events, (GFunc) gst_event_unref, - NULL); - g_list_free (basesrc->priv->pending_events); - basesrc->priv->pending_events = NULL; - g_atomic_int_set (&basesrc->priv->have_events, FALSE); - } - GST_OBJECT_UNLOCK (basesrc); - } - - GST_LIVE_SIGNAL (basesrc); - GST_LIVE_UNLOCK (basesrc); - - if (!flushing) { - /* Now wait for the stream lock to be released and clear our unlock request */ - GST_PAD_STREAM_LOCK (basesrc->srcpad); - if (bclass->unlock_stop) - bclass->unlock_stop (basesrc); - GST_PAD_STREAM_UNLOCK (basesrc->srcpad); - } - - return TRUE; -} - -/* the purpose of this function is to make sure that a live source blocks in the - * LIVE lock or leaves the LIVE lock and continues playing. */ -static gboolean -gst_base_src_set_playing (GstBaseSrc * basesrc, gboolean live_play) -{ - /* we are now able to grab the LIVE lock, when we get it, we can be - * waiting for PLAYING while blocked in the LIVE cond or we can be waiting - * for the clock. */ - GST_LIVE_LOCK (basesrc); - GST_DEBUG_OBJECT (basesrc, "unschedule clock"); - - /* unblock clock sync (if any) */ - if (basesrc->clock_id) - gst_clock_id_unschedule (basesrc->clock_id); - - /* configure what to do when we get to the LIVE lock. */ - GST_DEBUG_OBJECT (basesrc, "live running %d", live_play); - basesrc->live_running = live_play; - - if (live_play) { - gboolean start; - - /* for live sources we restart the timestamp correction */ - GST_OBJECT_LOCK (basesrc); - basesrc->priv->latency = -1; - GST_OBJECT_UNLOCK (basesrc); - /* have to restart the task in case it stopped because of the unlock when - * we went to PAUSED. Only do this if we operating in push mode. */ - GST_OBJECT_LOCK (basesrc->srcpad); - start = (GST_PAD_MODE (basesrc->srcpad) == GST_PAD_MODE_PUSH); - GST_OBJECT_UNLOCK (basesrc->srcpad); - if (start) - gst_pad_start_task (basesrc->srcpad, (GstTaskFunction) gst_base_src_loop, - basesrc->srcpad, NULL); - GST_DEBUG_OBJECT (basesrc, "signal"); - GST_LIVE_SIGNAL (basesrc); - } - GST_LIVE_UNLOCK (basesrc); - - return TRUE; -} - -static gboolean -gst_base_src_activate_push (GstPad * pad, GstObject * parent, gboolean active) -{ - GstBaseSrc *basesrc; - - basesrc = GST_BASE_SRC (parent); - - /* prepare subclass first */ - if (active) { - GST_DEBUG_OBJECT (basesrc, "Activating in push mode"); - - if (G_UNLIKELY (!basesrc->can_activate_push)) - goto no_push_activation; - - if (G_UNLIKELY (!gst_base_src_start (basesrc))) - goto error_start; - } else { - GST_DEBUG_OBJECT (basesrc, "Deactivating in push mode"); - /* now we can stop the source */ - if (G_UNLIKELY (!gst_base_src_stop (basesrc))) - goto error_stop; - } - return TRUE; - - /* ERRORS */ -no_push_activation: - { - GST_WARNING_OBJECT (basesrc, "Subclass disabled push-mode activation"); - return FALSE; - } -error_start: - { - GST_WARNING_OBJECT (basesrc, "Failed to start in push mode"); - return FALSE; - } -error_stop: - { - GST_DEBUG_OBJECT (basesrc, "Failed to stop in push mode"); - return FALSE; - } -} - -static gboolean -gst_base_src_activate_pull (GstPad * pad, GstObject * parent, gboolean active) -{ - GstBaseSrc *basesrc; - - basesrc = GST_BASE_SRC (parent); - - /* prepare subclass first */ - if (active) { - GST_DEBUG_OBJECT (basesrc, "Activating in pull mode"); - if (G_UNLIKELY (!gst_base_src_start (basesrc))) - goto error_start; - } else { - GST_DEBUG_OBJECT (basesrc, "Deactivating in pull mode"); - if (G_UNLIKELY (!gst_base_src_stop (basesrc))) - goto error_stop; - } - return TRUE; - - /* ERRORS */ -error_start: - { - GST_ERROR_OBJECT (basesrc, "Failed to start in pull mode"); - return FALSE; - } -error_stop: - { - GST_ERROR_OBJECT (basesrc, "Failed to stop in pull mode"); - return FALSE; - } -} - -static gboolean -gst_base_src_activate_mode (GstPad * pad, GstObject * parent, - GstPadMode mode, gboolean active) -{ - gboolean res; - GstBaseSrc *src = GST_BASE_SRC (parent); - - src->priv->stream_start_pending = FALSE; - - GST_DEBUG_OBJECT (pad, "activating in mode %d", mode); - - switch (mode) { - case GST_PAD_MODE_PULL: - res = gst_base_src_activate_pull (pad, parent, active); - break; - case GST_PAD_MODE_PUSH: - src->priv->stream_start_pending = active; - res = gst_base_src_activate_push (pad, parent, active); - break; - default: - GST_LOG_OBJECT (pad, "unknown activation mode %d", mode); - res = FALSE; - break; - } - return res; -} - - -static GstStateChangeReturn -gst_base_src_change_state (GstElement * element, GstStateChange transition) -{ - GstBaseSrc *basesrc; - GstStateChangeReturn result; - gboolean no_preroll = FALSE; - - basesrc = GST_BASE_SRC (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - no_preroll = gst_base_src_is_live (basesrc); - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - GST_DEBUG_OBJECT (basesrc, "PAUSED->PLAYING"); - if (gst_base_src_is_live (basesrc)) { - /* now we can start playback */ - gst_base_src_set_playing (basesrc, TRUE); - } - break; - default: - break; - } - - if ((result = - GST_ELEMENT_CLASS (parent_class)->change_state (element, - transition)) == GST_STATE_CHANGE_FAILURE) - goto failure; - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - GST_DEBUG_OBJECT (basesrc, "PLAYING->PAUSED"); - if (gst_base_src_is_live (basesrc)) { - /* make sure we block in the live cond in PAUSED */ - gst_base_src_set_playing (basesrc, FALSE); - no_preroll = TRUE; - } - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - { - /* we don't need to unblock anything here, the pad deactivation code - * already did this */ - if (g_atomic_int_get (&basesrc->priv->has_pending_eos)) { - GST_OBJECT_LOCK (basesrc); - CLEAR_PENDING_EOS (basesrc); - GST_OBJECT_UNLOCK (basesrc); - } - gst_event_replace (&basesrc->pending_seek, NULL); - break; - } - case GST_STATE_CHANGE_READY_TO_NULL: - break; - default: - break; - } - - if (no_preroll && result == GST_STATE_CHANGE_SUCCESS) - result = GST_STATE_CHANGE_NO_PREROLL; - - return result; - - /* ERRORS */ -failure: - { - GST_DEBUG_OBJECT (basesrc, "parent failed state change"); - return result; - } -} - -/** - * gst_base_src_get_buffer_pool: - * @src: a #GstBaseSrc - * - * Returns: (nullable) (transfer full): the instance of the #GstBufferPool used - * by the src; unref it after usage. - */ -GstBufferPool * -gst_base_src_get_buffer_pool (GstBaseSrc * src) -{ - GstBufferPool *ret = NULL; - - g_return_val_if_fail (GST_IS_BASE_SRC (src), NULL); - - GST_OBJECT_LOCK (src); - if (src->priv->pool) - ret = gst_object_ref (src->priv->pool); - GST_OBJECT_UNLOCK (src); - - return ret; -} - -/** - * gst_base_src_get_allocator: - * @src: a #GstBaseSrc - * @allocator: (out) (optional) (nullable) (transfer full): the #GstAllocator - * used - * @params: (out caller-allocates) (optional): the #GstAllocationParams of @allocator - * - * Lets #GstBaseSrc sub-classes to know the memory @allocator - * used by the base class and its @params. - * - * Unref the @allocator after usage. - */ -void -gst_base_src_get_allocator (GstBaseSrc * src, - GstAllocator ** allocator, GstAllocationParams * params) -{ - g_return_if_fail (GST_IS_BASE_SRC (src)); - - GST_OBJECT_LOCK (src); - if (allocator) - *allocator = src->priv->allocator ? - gst_object_ref (src->priv->allocator) : NULL; - - if (params) - *params = src->priv->params; - GST_OBJECT_UNLOCK (src); -} - -/** - * gst_base_src_submit_buffer_list: - * @src: a #GstBaseSrc - * @buffer_list: (transfer full): a #GstBufferList - * - * Subclasses can call this from their create virtual method implementation - * to submit a buffer list to be pushed out later. This is useful in - * cases where the create function wants to produce multiple buffers to be - * pushed out in one go in form of a #GstBufferList, which can reduce overhead - * drastically, especially for packetised inputs (for data streams where - * the packetisation/chunking is not important it is usually more efficient - * to return larger buffers instead). - * - * Subclasses that use this function from their create function must return - * %GST_FLOW_OK and no buffer from their create virtual method implementation. - * If a buffer is returned after a buffer list has also been submitted via this - * function the behaviour is undefined. - * - * Subclasses must only call this function once per create function call and - * subclasses must only call this function when the source operates in push - * mode. - * - * Since: 1.14 - */ -void -gst_base_src_submit_buffer_list (GstBaseSrc * src, GstBufferList * buffer_list) -{ - g_return_if_fail (GST_IS_BASE_SRC (src)); - g_return_if_fail (GST_IS_BUFFER_LIST (buffer_list)); - g_return_if_fail (BASE_SRC_HAS_PENDING_BUFFER_LIST (src) == FALSE); - - /* we need it to be writable later in get_range() where we use get_writable */ - src->priv->pending_bufferlist = gst_buffer_list_make_writable (buffer_list); - - GST_LOG_OBJECT (src, "%u buffers submitted in buffer list", - gst_buffer_list_length (buffer_list)); -} diff --git a/libs/gst/base/gstbasesrc.h b/libs/gst/base/gstbasesrc.h deleted file mode 100644 index 04304bfd06..0000000000 --- a/libs/gst/base/gstbasesrc.h +++ /dev/null @@ -1,334 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> - * 2000 Wim Taymans <wtay@chello.be> - * 2005 Wim Taymans <wim@fluendo.com> - * - * gstbasesrc.h: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_BASE_SRC_H__ -#define __GST_BASE_SRC_H__ - -#include <gst/gst.h> -#include <gst/base/base-prelude.h> - -G_BEGIN_DECLS - -#define GST_TYPE_BASE_SRC (gst_base_src_get_type()) -#define GST_BASE_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASE_SRC,GstBaseSrc)) -#define GST_BASE_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASE_SRC,GstBaseSrcClass)) -#define GST_BASE_SRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_BASE_SRC, GstBaseSrcClass)) -#define GST_IS_BASE_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASE_SRC)) -#define GST_IS_BASE_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASE_SRC)) -#define GST_BASE_SRC_CAST(obj) ((GstBaseSrc *)(obj)) - -/** - * GstBaseSrcFlags: - * @GST_BASE_SRC_FLAG_STARTING: has source is starting - * @GST_BASE_SRC_FLAG_STARTED: has source been started - * @GST_BASE_SRC_FLAG_LAST: offset to define more flags - * - * The #GstElement flags that a basesrc element may have. - */ -typedef enum { - GST_BASE_SRC_FLAG_STARTING = (GST_ELEMENT_FLAG_LAST << 0), - GST_BASE_SRC_FLAG_STARTED = (GST_ELEMENT_FLAG_LAST << 1), - /* padding */ - GST_BASE_SRC_FLAG_LAST = (GST_ELEMENT_FLAG_LAST << 6) -} GstBaseSrcFlags; - -#define GST_BASE_SRC_IS_STARTING(obj) GST_OBJECT_FLAG_IS_SET ((obj), GST_BASE_SRC_FLAG_STARTING) -#define GST_BASE_SRC_IS_STARTED(obj) GST_OBJECT_FLAG_IS_SET ((obj), GST_BASE_SRC_FLAG_STARTED) - -typedef struct _GstBaseSrc GstBaseSrc; -typedef struct _GstBaseSrcClass GstBaseSrcClass; -typedef struct _GstBaseSrcPrivate GstBaseSrcPrivate; - -/** - * GST_BASE_SRC_PAD: - * @obj: base source instance - * - * Gives the pointer to the #GstPad object of the element. - */ -#define GST_BASE_SRC_PAD(obj) (GST_BASE_SRC_CAST (obj)->srcpad) - - -/** - * GstBaseSrc: - * - * The opaque #GstBaseSrc data structure. - */ -struct _GstBaseSrc { - GstElement element; - - /*< protected >*/ - GstPad *srcpad; - - /* available to subclass implementations */ - /* MT-protected (with LIVE_LOCK) */ - GMutex live_lock; - GCond live_cond; - gboolean is_live; - gboolean live_running; - - /* MT-protected (with LOCK) */ - guint blocksize; /* size of buffers when operating push based */ - gboolean can_activate_push; /* some scheduling properties */ - gboolean random_access; - - GstClockID clock_id; /* for syncing */ - - /* MT-protected (with STREAM_LOCK *and* OBJECT_LOCK) */ - GstSegment segment; - /* MT-protected (with STREAM_LOCK) */ - gboolean need_newsegment; - - gint num_buffers; - gint num_buffers_left; - -#ifndef GST_REMOVE_DEPRECATED - gboolean typefind; /* unused */ -#endif - - gboolean running; - GstEvent *pending_seek; - - GstBaseSrcPrivate *priv; - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING_LARGE]; -}; - -/** - * GstBaseSrcClass: - * @parent_class: Element parent class - * @get_caps: Called to get the caps to report - * @negotiate: Negotiated the caps with the peer. - * @fixate: Called during negotiation if caps need fixating. Implement instead of - * setting a fixate function on the source pad. - * @set_caps: Notify subclass of changed output caps - * @decide_allocation: configure the allocation query - * @start: Start processing. Subclasses should open resources and prepare - * to produce data. Implementation should call gst_base_src_start_complete() - * when the operation completes, either from the current thread or any other - * thread that finishes the start operation asynchronously. - * @stop: Stop processing. Subclasses should use this to close resources. - * @get_times: Given a buffer, return the start and stop time when it - * should be pushed out. The base class will sync on the clock using - * these times. - * @get_size: Return the total size of the resource, in the format set by - * gst_base_src_set_format(). - * @is_seekable: Check if the source can seek - * @prepare_seek_segment: Prepare the #GstSegment that will be passed to the - * #GstBaseSrcClass::do_seek vmethod for executing a seek - * request. Sub-classes should override this if they support seeking in - * formats other than the configured native format. By default, it tries to - * convert the seek arguments to the configured native format and prepare a - * segment in that format. - * @do_seek: Perform seeking on the resource to the indicated segment. - * @unlock: Unlock any pending access to the resource. Subclasses should unblock - * any blocked function ASAP. In particular, any `create()` function in - * progress should be unblocked and should return GST_FLOW_FLUSHING. Any - * future #GstBaseSrcClass::create function call should also return - * GST_FLOW_FLUSHING until the #GstBaseSrcClass::unlock_stop function has - * been called. - * @unlock_stop: Clear the previous unlock request. Subclasses should clear any - * state they set during #GstBaseSrcClass::unlock, such as clearing command - * queues. - * @query: Handle a requested query. - * @event: Override this to implement custom event handling. - * @create: Ask the subclass to create a buffer with offset and size. When the - * subclass returns GST_FLOW_OK, it MUST return a buffer of the requested size - * unless fewer bytes are available because an EOS condition is near. No - * buffer should be returned when the return value is different from - * GST_FLOW_OK. A return value of GST_FLOW_EOS signifies that the end of - * stream is reached. The default implementation will call - * #GstBaseSrcClass::alloc and then call #GstBaseSrcClass::fill. - * @alloc: Ask the subclass to allocate a buffer with for offset and size. The - * default implementation will create a new buffer from the negotiated allocator. - * @fill: Ask the subclass to fill the buffer with data for offset and size. The - * passed buffer is guaranteed to hold the requested amount of bytes. - * - * Subclasses can override any of the available virtual methods or not, as - * needed. At the minimum, the @create method should be overridden to produce - * buffers. - */ -struct _GstBaseSrcClass { - GstElementClass parent_class; - - /*< public >*/ - /* virtual methods for subclasses */ - - /** - * GstBaseSrcClass::get_caps: - * @filter: (in) (nullable): - * - * Called to get the caps to report. - */ - GstCaps* (*get_caps) (GstBaseSrc *src, GstCaps *filter); - /* decide on caps */ - gboolean (*negotiate) (GstBaseSrc *src); - /* called if, in negotiation, caps need fixating */ - GstCaps * (*fixate) (GstBaseSrc *src, GstCaps *caps); - /* notify the subclass of new caps */ - gboolean (*set_caps) (GstBaseSrc *src, GstCaps *caps); - - /* setup allocation query */ - gboolean (*decide_allocation) (GstBaseSrc *src, GstQuery *query); - - /* start and stop processing, ideal for opening/closing the resource */ - gboolean (*start) (GstBaseSrc *src); - gboolean (*stop) (GstBaseSrc *src); - - /** - * GstBaseSrcClass::get_times: - * @start: (out): - * @end: (out): - * - * Given @buffer, return @start and @end time when it should be pushed - * out. The base class will sync on the clock using these times. - */ - void (*get_times) (GstBaseSrc *src, GstBuffer *buffer, - GstClockTime *start, GstClockTime *end); - - /* get the total size of the resource in the format set by - * gst_base_src_set_format() */ - gboolean (*get_size) (GstBaseSrc *src, guint64 *size); - - /* check if the resource is seekable */ - gboolean (*is_seekable) (GstBaseSrc *src); - - /* Prepare the segment on which to perform do_seek(), converting to the - * current basesrc format. */ - gboolean (*prepare_seek_segment) (GstBaseSrc *src, GstEvent *seek, - GstSegment *segment); - /* notify subclasses of a seek */ - gboolean (*do_seek) (GstBaseSrc *src, GstSegment *segment); - - /* unlock any pending access to the resource. subclasses should unlock - * any function ASAP. */ - gboolean (*unlock) (GstBaseSrc *src); - /* Clear any pending unlock request, as we succeeded in unlocking */ - gboolean (*unlock_stop) (GstBaseSrc *src); - - /* notify subclasses of a query */ - gboolean (*query) (GstBaseSrc *src, GstQuery *query); - - /* notify subclasses of an event */ - gboolean (*event) (GstBaseSrc *src, GstEvent *event); - - /** - * GstBaseSrcClass::create: - * @buf: (out): - * - * Ask the subclass to create a buffer with @offset and @size, the default - * implementation will call alloc and fill. - */ - GstFlowReturn (*create) (GstBaseSrc *src, guint64 offset, guint size, - GstBuffer **buf); - /* ask the subclass to allocate an output buffer. The default implementation - * will use the negotiated allocator. */ - GstFlowReturn (*alloc) (GstBaseSrc *src, guint64 offset, guint size, - GstBuffer **buf); - /* ask the subclass to fill the buffer with data from offset and size */ - GstFlowReturn (*fill) (GstBaseSrc *src, guint64 offset, guint size, - GstBuffer *buf); - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING_LARGE]; -}; - -GST_BASE_API -GType gst_base_src_get_type (void); - -GST_BASE_API -GstFlowReturn gst_base_src_wait_playing (GstBaseSrc *src); - -GST_BASE_API -void gst_base_src_set_live (GstBaseSrc *src, gboolean live); - -GST_BASE_API -gboolean gst_base_src_is_live (GstBaseSrc *src); - -GST_BASE_API -void gst_base_src_set_format (GstBaseSrc *src, GstFormat format); - -GST_BASE_API -void gst_base_src_set_dynamic_size (GstBaseSrc * src, gboolean dynamic); - -GST_BASE_API -void gst_base_src_set_automatic_eos (GstBaseSrc * src, gboolean automatic_eos); - -GST_BASE_API -void gst_base_src_set_async (GstBaseSrc *src, gboolean async); - -GST_BASE_API -gboolean gst_base_src_is_async (GstBaseSrc *src); - -GST_BASE_API -gboolean gst_base_src_negotiate (GstBaseSrc *src); - -GST_BASE_API -void gst_base_src_start_complete (GstBaseSrc * basesrc, GstFlowReturn ret); - -GST_BASE_API -GstFlowReturn gst_base_src_start_wait (GstBaseSrc * basesrc); - -GST_BASE_API -gboolean gst_base_src_query_latency (GstBaseSrc *src, gboolean * live, - GstClockTime * min_latency, - GstClockTime * max_latency); -GST_BASE_API -void gst_base_src_set_blocksize (GstBaseSrc *src, guint blocksize); - -GST_BASE_API -guint gst_base_src_get_blocksize (GstBaseSrc *src); - -GST_BASE_API -void gst_base_src_set_do_timestamp (GstBaseSrc *src, gboolean timestamp); - -GST_BASE_API -gboolean gst_base_src_get_do_timestamp (GstBaseSrc *src); - -GST_BASE_API -gboolean gst_base_src_new_seamless_segment (GstBaseSrc *src, gint64 start, gint64 stop, gint64 time); - -GST_BASE_API -gboolean gst_base_src_new_segment (GstBaseSrc *src, - const GstSegment * segment); - -GST_BASE_API -gboolean gst_base_src_set_caps (GstBaseSrc *src, GstCaps *caps); - -GST_BASE_API -GstBufferPool * gst_base_src_get_buffer_pool (GstBaseSrc *src); - -GST_BASE_API -void gst_base_src_get_allocator (GstBaseSrc *src, - GstAllocator **allocator, - GstAllocationParams *params); - -GST_BASE_API -void gst_base_src_submit_buffer_list (GstBaseSrc * src, - GstBufferList * buffer_list); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstBaseSrc, gst_object_unref) - -G_END_DECLS - -#endif /* __GST_BASE_SRC_H__ */ diff --git a/libs/gst/base/gstbasetransform.c b/libs/gst/base/gstbasetransform.c deleted file mode 100644 index e4e7ee6415..0000000000 --- a/libs/gst/base/gstbasetransform.c +++ /dev/null @@ -1,2921 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> - * 2005 Wim Taymans <wim@fluendo.com> - * 2005 Andy Wingo <wingo@fluendo.com> - * 2005 Thomas Vander Stichele <thomas at apestaart dot org> - * 2008 Wim Taymans <wim.taymans@gmail.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * SECTION:gstbasetransform - * @title: GstBaseTransform - * @short_description: Base class for simple transform filters - * @see_also: #GstBaseSrc, #GstBaseSink - * - * This base class is for filter elements that process data. Elements - * that are suitable for implementation using #GstBaseTransform are ones - * where the size and caps of the output is known entirely from the input - * caps and buffer sizes. These include elements that directly transform - * one buffer into another, modify the contents of a buffer in-place, as - * well as elements that collate multiple input buffers into one output buffer, - * or that expand one input buffer into multiple output buffers. See below - * for more concrete use cases. - * - * It provides for: - * - * * one sinkpad and one srcpad - * * Possible formats on sink and source pad implemented - * with custom transform_caps function. By default uses - * same format on sink and source. - * - * * Handles state changes - * * Does flushing - * * Push mode - * * Pull mode if the sub-class transform can operate on arbitrary data - * - * # Use Cases - * - * ## Passthrough mode - * - * * Element has no interest in modifying the buffer. It may want to inspect it, - * in which case the element should have a transform_ip function. If there - * is no transform_ip function in passthrough mode, the buffer is pushed - * intact. - * - * * The #GstBaseTransformClass.passthrough_on_same_caps variable - * will automatically set/unset passthrough based on whether the - * element negotiates the same caps on both pads. - * - * * #GstBaseTransformClass.passthrough_on_same_caps on an element that - * doesn't implement a transform_caps function is useful for elements that - * only inspect data (such as level) - * - * * Example elements - * - * * Level - * * Videoscale, audioconvert, videoconvert, audioresample in certain modes. - * - * ## Modifications in-place - input buffer and output buffer are the same thing. - * - * * The element must implement a transform_ip function. - * * Output buffer size must <= input buffer size - * * If the always_in_place flag is set, non-writable buffers will be copied - * and passed to the transform_ip function, otherwise a new buffer will be - * created and the transform function called. - * - * * Incoming writable buffers will be passed to the transform_ip function - * immediately. - * * only implementing transform_ip and not transform implies always_in_place = %TRUE - * - * * Example elements: - * * Volume - * * Audioconvert in certain modes (signed/unsigned conversion) - * * videoconvert in certain modes (endianness swapping) - * - * ## Modifications only to the caps/metadata of a buffer - * - * * The element does not require writable data, but non-writable buffers - * should be subbuffered so that the meta-information can be replaced. - * - * * Elements wishing to operate in this mode should replace the - * prepare_output_buffer method to create subbuffers of the input buffer - * and set always_in_place to %TRUE - * - * * Example elements - * * Capsfilter when setting caps on outgoing buffers that have - * none. - * * identity when it is going to re-timestamp buffers by - * datarate. - * - * ## Normal mode - * * always_in_place flag is not set, or there is no transform_ip function - * * Element will receive an input buffer and output buffer to operate on. - * * Output buffer is allocated by calling the prepare_output_buffer function. - * * Example elements: - * * Videoscale, videoconvert, audioconvert when doing - * scaling/conversions - * - * ## Special output buffer allocations - * * Elements which need to do special allocation of their output buffers - * beyond allocating output buffers via the negotiated allocator or - * buffer pool should implement the prepare_output_buffer method. - * - * * Example elements: - * * efence - * - * # Sub-class settable flags on GstBaseTransform - * - * * passthrough - * - * * Implies that in the current configuration, the sub-class is not interested in modifying the buffers. - * * Elements which are always in passthrough mode whenever the same caps has been negotiated on both pads can set the class variable passthrough_on_same_caps to have this behaviour automatically. - * - * * always_in_place - * * Determines whether a non-writable buffer will be copied before passing - * to the transform_ip function. - * - * * Implied %TRUE if no transform function is implemented. - * * Implied %FALSE if ONLY transform function is implemented. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include <stdlib.h> -#include <string.h> - -#include "../../../gst/gst_private.h" -#include "../../../gst/gst-i18n-lib.h" -#include "../../../gst/glib-compat-private.h" -#include "gstbasetransform.h" - -GST_DEBUG_CATEGORY_STATIC (gst_base_transform_debug); -#define GST_CAT_DEFAULT gst_base_transform_debug - -/* BaseTransform signals and args */ -enum -{ - /* FILL ME */ - LAST_SIGNAL -}; - -#define DEFAULT_PROP_QOS FALSE - -enum -{ - PROP_0, - PROP_QOS -}; - -struct _GstBaseTransformPrivate -{ - /* Set by sub-class */ - gboolean passthrough; - gboolean always_in_place; - - GstCaps *cache_caps1; - gsize cache_caps1_size; - GstCaps *cache_caps2; - gsize cache_caps2_size; - gboolean have_same_caps; - - gboolean negotiated; - - /* QoS *//* with LOCK */ - gboolean qos_enabled; - gdouble proportion; - GstClockTime earliest_time; - /* previous buffer had a discont */ - gboolean discont; - - GstPadMode pad_mode; - - gboolean gap_aware; - gboolean prefer_passthrough; - - /* QoS stats */ - guint64 processed; - guint64 dropped; - - GstClockTime position_out; - - GstBufferPool *pool; - gboolean pool_active; - GstAllocator *allocator; - GstAllocationParams params; - GstQuery *query; -}; - - -static GstElementClass *parent_class = NULL; -static gint private_offset = 0; - -static void gst_base_transform_class_init (GstBaseTransformClass * klass); -static void gst_base_transform_init (GstBaseTransform * trans, - GstBaseTransformClass * klass); -static GstFlowReturn default_submit_input_buffer (GstBaseTransform * trans, - gboolean is_discont, GstBuffer * input); -static GstFlowReturn default_generate_output (GstBaseTransform * trans, - GstBuffer ** outbuf); - -/* we can't use G_DEFINE_ABSTRACT_TYPE because we need the klass in the _init - * method to get to the padtemplates */ -GType -gst_base_transform_get_type (void) -{ - static gsize base_transform_type = 0; - - if (g_once_init_enter (&base_transform_type)) { - GType _type; - static const GTypeInfo base_transform_info = { - sizeof (GstBaseTransformClass), - NULL, - NULL, - (GClassInitFunc) gst_base_transform_class_init, - NULL, - NULL, - sizeof (GstBaseTransform), - 0, - (GInstanceInitFunc) gst_base_transform_init, - }; - - _type = g_type_register_static (GST_TYPE_ELEMENT, - "GstBaseTransform", &base_transform_info, G_TYPE_FLAG_ABSTRACT); - - private_offset = - g_type_add_instance_private (_type, sizeof (GstBaseTransformPrivate)); - - g_once_init_leave (&base_transform_type, _type); - } - return base_transform_type; -} - -static inline GstBaseTransformPrivate * -gst_base_transform_get_instance_private (GstBaseTransform * self) -{ - return (G_STRUCT_MEMBER_P (self, private_offset)); -} - -static void gst_base_transform_finalize (GObject * object); -static void gst_base_transform_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_base_transform_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static gboolean gst_base_transform_src_activate_mode (GstPad * pad, - GstObject * parent, GstPadMode mode, gboolean active); -static gboolean gst_base_transform_sink_activate_mode (GstPad * pad, - GstObject * parent, GstPadMode mode, gboolean active); -static gboolean gst_base_transform_activate (GstBaseTransform * trans, - gboolean active); -static gboolean gst_base_transform_get_unit_size (GstBaseTransform * trans, - GstCaps * caps, gsize * size); - -static gboolean gst_base_transform_src_event (GstPad * pad, GstObject * parent, - GstEvent * event); -static gboolean gst_base_transform_src_eventfunc (GstBaseTransform * trans, - GstEvent * event); -static gboolean gst_base_transform_sink_event (GstPad * pad, GstObject * parent, - GstEvent * event); -static gboolean gst_base_transform_sink_eventfunc (GstBaseTransform * trans, - GstEvent * event); -static GstFlowReturn gst_base_transform_getrange (GstPad * pad, - GstObject * parent, guint64 offset, guint length, GstBuffer ** buffer); -static GstFlowReturn gst_base_transform_chain (GstPad * pad, GstObject * parent, - GstBuffer * buffer); -static GstCaps *gst_base_transform_default_transform_caps (GstBaseTransform * - trans, GstPadDirection direction, GstCaps * caps, GstCaps * filter); -static GstCaps *gst_base_transform_default_fixate_caps (GstBaseTransform * - trans, GstPadDirection direction, GstCaps * caps, GstCaps * othercaps); -static GstCaps *gst_base_transform_query_caps (GstBaseTransform * trans, - GstPad * pad, GstCaps * filter); -static gboolean gst_base_transform_acceptcaps_default (GstBaseTransform * trans, - GstPadDirection direction, GstCaps * caps); -static gboolean gst_base_transform_setcaps (GstBaseTransform * trans, - GstPad * pad, GstCaps * caps); -static gboolean gst_base_transform_default_decide_allocation (GstBaseTransform - * trans, GstQuery * query); -static gboolean gst_base_transform_default_propose_allocation (GstBaseTransform - * trans, GstQuery * decide_query, GstQuery * query); -static gboolean gst_base_transform_query (GstPad * pad, GstObject * parent, - GstQuery * query); -static gboolean gst_base_transform_default_query (GstBaseTransform * trans, - GstPadDirection direction, GstQuery * query); -static gboolean gst_base_transform_default_transform_size (GstBaseTransform * - trans, GstPadDirection direction, GstCaps * caps, gsize size, - GstCaps * othercaps, gsize * othersize); - -static GstFlowReturn default_prepare_output_buffer (GstBaseTransform * trans, - GstBuffer * inbuf, GstBuffer ** outbuf); -static gboolean default_copy_metadata (GstBaseTransform * trans, - GstBuffer * inbuf, GstBuffer * outbuf); -static gboolean -gst_base_transform_default_transform_meta (GstBaseTransform * trans, - GstBuffer * outbuf, GstMeta * meta, GstBuffer * inbuf); - -/* static guint gst_base_transform_signals[LAST_SIGNAL] = { 0 }; */ - - -static void -gst_base_transform_finalize (GObject * object) -{ - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_base_transform_class_init (GstBaseTransformClass * klass) -{ - GObjectClass *gobject_class; - - gobject_class = G_OBJECT_CLASS (klass); - - if (private_offset != 0) - g_type_class_adjust_private_offset (klass, &private_offset); - - GST_DEBUG_CATEGORY_INIT (gst_base_transform_debug, "basetransform", 0, - "basetransform element"); - - GST_DEBUG ("gst_base_transform_class_init"); - - parent_class = g_type_class_peek_parent (klass); - - gobject_class->set_property = gst_base_transform_set_property; - gobject_class->get_property = gst_base_transform_get_property; - - g_object_class_install_property (gobject_class, PROP_QOS, - g_param_spec_boolean ("qos", "QoS", "Handle Quality-of-Service events", - DEFAULT_PROP_QOS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - gobject_class->finalize = gst_base_transform_finalize; - - klass->passthrough_on_same_caps = FALSE; - klass->transform_ip_on_passthrough = TRUE; - - klass->transform_caps = - GST_DEBUG_FUNCPTR (gst_base_transform_default_transform_caps); - klass->fixate_caps = - GST_DEBUG_FUNCPTR (gst_base_transform_default_fixate_caps); - klass->accept_caps = - GST_DEBUG_FUNCPTR (gst_base_transform_acceptcaps_default); - klass->query = GST_DEBUG_FUNCPTR (gst_base_transform_default_query); - klass->decide_allocation = - GST_DEBUG_FUNCPTR (gst_base_transform_default_decide_allocation); - klass->propose_allocation = - GST_DEBUG_FUNCPTR (gst_base_transform_default_propose_allocation); - klass->transform_size = - GST_DEBUG_FUNCPTR (gst_base_transform_default_transform_size); - klass->transform_meta = - GST_DEBUG_FUNCPTR (gst_base_transform_default_transform_meta); - - klass->sink_event = GST_DEBUG_FUNCPTR (gst_base_transform_sink_eventfunc); - klass->src_event = GST_DEBUG_FUNCPTR (gst_base_transform_src_eventfunc); - klass->prepare_output_buffer = - GST_DEBUG_FUNCPTR (default_prepare_output_buffer); - klass->copy_metadata = GST_DEBUG_FUNCPTR (default_copy_metadata); - klass->submit_input_buffer = GST_DEBUG_FUNCPTR (default_submit_input_buffer); - klass->generate_output = GST_DEBUG_FUNCPTR (default_generate_output); -} - -static void -gst_base_transform_init (GstBaseTransform * trans, - GstBaseTransformClass * bclass) -{ - GstPadTemplate *pad_template; - GstBaseTransformPrivate *priv; - - GST_DEBUG ("gst_base_transform_init"); - - priv = trans->priv = gst_base_transform_get_instance_private (trans); - - pad_template = - gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "sink"); - g_return_if_fail (pad_template != NULL); - trans->sinkpad = gst_pad_new_from_template (pad_template, "sink"); - gst_pad_set_event_function (trans->sinkpad, - GST_DEBUG_FUNCPTR (gst_base_transform_sink_event)); - gst_pad_set_chain_function (trans->sinkpad, - GST_DEBUG_FUNCPTR (gst_base_transform_chain)); - gst_pad_set_activatemode_function (trans->sinkpad, - GST_DEBUG_FUNCPTR (gst_base_transform_sink_activate_mode)); - gst_pad_set_query_function (trans->sinkpad, - GST_DEBUG_FUNCPTR (gst_base_transform_query)); - gst_element_add_pad (GST_ELEMENT (trans), trans->sinkpad); - - pad_template = - gst_element_class_get_pad_template (GST_ELEMENT_CLASS (bclass), "src"); - g_return_if_fail (pad_template != NULL); - trans->srcpad = gst_pad_new_from_template (pad_template, "src"); - gst_pad_set_event_function (trans->srcpad, - GST_DEBUG_FUNCPTR (gst_base_transform_src_event)); - gst_pad_set_getrange_function (trans->srcpad, - GST_DEBUG_FUNCPTR (gst_base_transform_getrange)); - gst_pad_set_activatemode_function (trans->srcpad, - GST_DEBUG_FUNCPTR (gst_base_transform_src_activate_mode)); - gst_pad_set_query_function (trans->srcpad, - GST_DEBUG_FUNCPTR (gst_base_transform_query)); - gst_element_add_pad (GST_ELEMENT (trans), trans->srcpad); - - priv->qos_enabled = DEFAULT_PROP_QOS; - priv->cache_caps1 = NULL; - priv->cache_caps2 = NULL; - priv->pad_mode = GST_PAD_MODE_NONE; - priv->gap_aware = FALSE; - priv->prefer_passthrough = TRUE; - - priv->passthrough = FALSE; - if (bclass->transform == NULL) { - /* If no transform function, always_in_place is TRUE */ - GST_DEBUG_OBJECT (trans, "setting in_place TRUE"); - priv->always_in_place = TRUE; - - if (bclass->transform_ip == NULL) { - GST_DEBUG_OBJECT (trans, "setting passthrough TRUE"); - priv->passthrough = TRUE; - } - } - - priv->processed = 0; - priv->dropped = 0; -} - -static GstCaps * -gst_base_transform_default_transform_caps (GstBaseTransform * trans, - GstPadDirection direction, GstCaps * caps, GstCaps * filter) -{ - GstCaps *ret; - - GST_DEBUG_OBJECT (trans, "identity from: %" GST_PTR_FORMAT, caps); - /* no transform function, use the identity transform */ - if (filter) { - ret = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST); - } else { - ret = gst_caps_ref (caps); - } - return ret; -} - -/* given @caps on the src or sink pad (given by @direction) - * calculate the possible caps on the other pad. - * - * Returns new caps, unref after usage. - */ -static GstCaps * -gst_base_transform_transform_caps (GstBaseTransform * trans, - GstPadDirection direction, GstCaps * caps, GstCaps * filter) -{ - GstCaps *ret = NULL; - GstBaseTransformClass *klass; - - if (caps == NULL) - return NULL; - - klass = GST_BASE_TRANSFORM_GET_CLASS (trans); - - /* if there is a custom transform function, use this */ - if (klass->transform_caps) { - GST_DEBUG_OBJECT (trans, "transform caps (direction = %d)", direction); - - GST_LOG_OBJECT (trans, "from: %" GST_PTR_FORMAT, caps); - ret = klass->transform_caps (trans, direction, caps, filter); - GST_LOG_OBJECT (trans, " to: %" GST_PTR_FORMAT, ret); - -#ifdef GST_ENABLE_EXTRA_CHECKS - if (filter) { - if (!gst_caps_is_subset (ret, filter)) { - GstCaps *intersection; - - GST_ERROR_OBJECT (trans, - "transform_caps returned caps %" GST_PTR_FORMAT - " which are not a real subset of the filter caps %" - GST_PTR_FORMAT, ret, filter); - g_warning ("%s: transform_caps returned caps which are not a real " - "subset of the filter caps", GST_ELEMENT_NAME (trans)); - - intersection = - gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST); - gst_caps_unref (ret); - ret = intersection; - } - } -#endif - } - - GST_DEBUG_OBJECT (trans, "to: %" GST_PTR_FORMAT, ret); - - return ret; -} - -static gboolean -gst_base_transform_default_transform_meta (GstBaseTransform * trans, - GstBuffer * outbuf, GstMeta * meta, GstBuffer * inbuf) -{ - const GstMetaInfo *info = meta->info; - const gchar *const *tags; - - tags = gst_meta_api_type_get_tags (info->api); - - if (!tags) - return TRUE; - - return FALSE; -} - -static gboolean -gst_base_transform_default_transform_size (GstBaseTransform * trans, - GstPadDirection direction, GstCaps * caps, gsize size, - GstCaps * othercaps, gsize * othersize) -{ - gsize inunitsize, outunitsize, units; - GstBaseTransformClass *klass; - - klass = GST_BASE_TRANSFORM_GET_CLASS (trans); - - if (klass->get_unit_size == NULL) { - /* if there is no transform_size and no unit_size, it means the - * element does not modify the size of a buffer */ - *othersize = size; - } else { - /* there is no transform_size function, we have to use the unit_size - * functions. This method assumes there is a fixed unit_size associated with - * each caps. We provide the same amount of units on both sides. */ - if (!gst_base_transform_get_unit_size (trans, caps, &inunitsize)) - goto no_in_size; - - GST_DEBUG_OBJECT (trans, - "input size %" G_GSIZE_FORMAT ", input unit size %" G_GSIZE_FORMAT, - size, inunitsize); - - /* input size must be a multiple of the unit_size of the input caps */ - if (inunitsize == 0 || (size % inunitsize != 0)) - goto no_multiple; - - /* get the amount of units */ - units = size / inunitsize; - - /* now get the unit size of the output */ - if (!gst_base_transform_get_unit_size (trans, othercaps, &outunitsize)) - goto no_out_size; - - /* the output size is the unit_size times the amount of units on the - * input */ - *othersize = units * outunitsize; - GST_DEBUG_OBJECT (trans, "transformed size to %" G_GSIZE_FORMAT, - *othersize); - } - return TRUE; - - /* ERRORS */ -no_in_size: - { - GST_DEBUG_OBJECT (trans, "could not get in_size"); - g_warning ("%s: could not get in_size", GST_ELEMENT_NAME (trans)); - return FALSE; - } -no_multiple: - { - GST_DEBUG_OBJECT (trans, "Size %" G_GSIZE_FORMAT " is not a multiple of" - "unit size %" G_GSIZE_FORMAT, size, inunitsize); - g_warning ("%s: size %" G_GSIZE_FORMAT " is not a multiple of unit size %" - G_GSIZE_FORMAT, GST_ELEMENT_NAME (trans), size, inunitsize); - return FALSE; - } -no_out_size: - { - GST_DEBUG_OBJECT (trans, "could not get out_size"); - g_warning ("%s: could not get out_size", GST_ELEMENT_NAME (trans)); - return FALSE; - } -} - -/* transform a buffer of @size with @caps on the pad with @direction to - * the size of a buffer with @othercaps and store the result in @othersize - * - * We have two ways of doing this: - * 1) use a custom transform size function, this is for complicated custom - * cases with no fixed unit_size. - * 2) use the unit_size functions where there is a relationship between the - * caps and the size of a buffer. - */ -static gboolean -gst_base_transform_transform_size (GstBaseTransform * trans, - GstPadDirection direction, GstCaps * caps, - gsize size, GstCaps * othercaps, gsize * othersize) -{ - GstBaseTransformClass *klass; - gboolean ret = FALSE; - - klass = GST_BASE_TRANSFORM_GET_CLASS (trans); - - GST_DEBUG_OBJECT (trans, - "asked to transform size %" G_GSIZE_FORMAT " for caps %" - GST_PTR_FORMAT " to size for caps %" GST_PTR_FORMAT " in direction %s", - size, caps, othercaps, direction == GST_PAD_SRC ? "SRC" : "SINK"); - - if (klass->transform_size) { - /* if there is a custom transform function, use this */ - ret = klass->transform_size (trans, direction, caps, size, othercaps, - othersize); - } - return ret; -} - -/* get the caps that can be handled by @pad. We perform: - * - * - take the caps of peer of otherpad, - * - filter against the padtemplate of otherpad, - * - calculate all transforms of remaining caps - * - filter against template of @pad - * - * If there is no peer, we simply return the caps of the padtemplate of pad. - */ -static GstCaps * -gst_base_transform_query_caps (GstBaseTransform * trans, GstPad * pad, - GstCaps * filter) -{ - GstPad *otherpad; - GstCaps *peercaps = NULL, *caps, *temp, *peerfilter = NULL; - GstCaps *templ, *otempl; - - otherpad = (pad == trans->srcpad) ? trans->sinkpad : trans->srcpad; - - templ = gst_pad_get_pad_template_caps (pad); - otempl = gst_pad_get_pad_template_caps (otherpad); - - /* first prepare the filter to be sent onwards. We need to filter and - * transform it to valid caps for the otherpad. */ - if (filter) { - GST_DEBUG_OBJECT (pad, "filter caps %" GST_PTR_FORMAT, filter); - - /* filtered against our padtemplate of this pad */ - GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, templ); - temp = gst_caps_intersect_full (filter, templ, GST_CAPS_INTERSECT_FIRST); - GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp); - - /* then see what we can transform this to */ - peerfilter = gst_base_transform_transform_caps (trans, - GST_PAD_DIRECTION (pad), temp, NULL); - GST_DEBUG_OBJECT (pad, "transformed %" GST_PTR_FORMAT, peerfilter); - gst_caps_unref (temp); - - if (peerfilter) { - if (!gst_caps_is_empty (peerfilter)) { - /* and filter against the template of the other pad */ - GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, otempl); - /* We keep the caps sorted like the returned caps */ - temp = - gst_caps_intersect_full (peerfilter, otempl, - GST_CAPS_INTERSECT_FIRST); - GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp); - gst_caps_unref (peerfilter); - peerfilter = temp; - } - - /* If we filter out everything, bail out */ - if (peerfilter && gst_caps_is_empty (peerfilter)) { - GST_DEBUG_OBJECT (pad, "peer filter caps are empty"); - caps = peerfilter; - peerfilter = NULL; - goto done; - } - } - } - - GST_DEBUG_OBJECT (pad, "peer filter caps %" GST_PTR_FORMAT, peerfilter); - - /* query the peer with the transformed filter */ - peercaps = gst_pad_peer_query_caps (otherpad, peerfilter); - - if (peerfilter) - gst_caps_unref (peerfilter); - - if (peercaps) { - GST_DEBUG_OBJECT (pad, "peer caps %" GST_PTR_FORMAT, peercaps); - - /* filtered against our padtemplate on the other side */ - GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, otempl); - temp = gst_caps_intersect_full (peercaps, otempl, GST_CAPS_INTERSECT_FIRST); - GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp); - } else { - temp = gst_caps_ref (otempl); - } - - /* then see what we can transform this to */ - caps = gst_base_transform_transform_caps (trans, - GST_PAD_DIRECTION (otherpad), temp, filter); - GST_DEBUG_OBJECT (pad, "transformed %" GST_PTR_FORMAT, caps); - gst_caps_unref (temp); - if (caps == NULL || gst_caps_is_empty (caps)) - goto done; - - if (peercaps) { - /* and filter against the template of this pad */ - GST_DEBUG_OBJECT (pad, "our template %" GST_PTR_FORMAT, templ); - /* We keep the caps sorted like the returned caps */ - temp = gst_caps_intersect_full (caps, templ, GST_CAPS_INTERSECT_FIRST); - GST_DEBUG_OBJECT (pad, "intersected %" GST_PTR_FORMAT, temp); - gst_caps_unref (caps); - caps = temp; - - if (trans->priv->prefer_passthrough) { - /* Now try if we can put the untransformed downstream caps first */ - temp = gst_caps_intersect_full (peercaps, caps, GST_CAPS_INTERSECT_FIRST); - if (!gst_caps_is_empty (temp)) { - caps = gst_caps_merge (temp, caps); - } else { - gst_caps_unref (temp); - } - } - } else { - gst_caps_unref (caps); - /* no peer or the peer can do anything, our padtemplate is enough then */ - if (filter) { - caps = gst_caps_intersect_full (filter, templ, GST_CAPS_INTERSECT_FIRST); - } else { - caps = gst_caps_ref (templ); - } - } - -done: - GST_DEBUG_OBJECT (trans, "returning %" GST_PTR_FORMAT, caps); - - if (peercaps) - gst_caps_unref (peercaps); - - gst_caps_unref (templ); - gst_caps_unref (otempl); - - return caps; -} - -/* takes ownership of the pool, allocator and query */ -static gboolean -gst_base_transform_set_allocation (GstBaseTransform * trans, - GstBufferPool * pool, GstAllocator * allocator, - const GstAllocationParams * params, GstQuery * query) -{ - GstAllocator *oldalloc; - GstBufferPool *oldpool; - GstQuery *oldquery; - GstBaseTransformPrivate *priv = trans->priv; - - GST_OBJECT_LOCK (trans); - oldpool = priv->pool; - priv->pool = pool; - priv->pool_active = FALSE; - - oldalloc = priv->allocator; - priv->allocator = allocator; - - oldquery = priv->query; - priv->query = query; - - if (params) - priv->params = *params; - else - gst_allocation_params_init (&priv->params); - GST_OBJECT_UNLOCK (trans); - - if (oldpool) { - GST_DEBUG_OBJECT (trans, "deactivating old pool %p", oldpool); - gst_buffer_pool_set_active (oldpool, FALSE); - gst_object_unref (oldpool); - } - if (oldalloc) { - gst_object_unref (oldalloc); - } - if (oldquery) { - gst_query_unref (oldquery); - } - return TRUE; -} - -static gboolean -gst_base_transform_default_decide_allocation (GstBaseTransform * trans, - GstQuery * query) -{ - guint i, n_metas; - GstBaseTransformClass *klass; - GstCaps *outcaps; - GstBufferPool *pool; - guint size, min, max; - GstAllocator *allocator; - GstAllocationParams params; - GstStructure *config; - gboolean update_allocator; - - klass = GST_BASE_TRANSFORM_GET_CLASS (trans); - - n_metas = gst_query_get_n_allocation_metas (query); - for (i = 0; i < n_metas; i++) { - GType api; - const GstStructure *params; - gboolean remove; - - api = gst_query_parse_nth_allocation_meta (query, i, ¶ms); - - /* by default we remove all metadata, subclasses should implement a - * filter_meta function */ - if (gst_meta_api_type_has_tag (api, _gst_meta_tag_memory)) { - /* remove all memory dependent metadata because we are going to have to - * allocate different memory for input and output. */ - GST_LOG_OBJECT (trans, "removing memory specific metadata %s", - g_type_name (api)); - remove = TRUE; - } else if (G_LIKELY (klass->filter_meta)) { - /* remove if the subclass said so */ - remove = !klass->filter_meta (trans, query, api, params); - GST_LOG_OBJECT (trans, "filter_meta for api %s returned: %s", - g_type_name (api), (remove ? "remove" : "keep")); - } else { - GST_LOG_OBJECT (trans, "removing metadata %s", g_type_name (api)); - remove = TRUE; - } - - if (remove) { - gst_query_remove_nth_allocation_meta (query, i); - i--; - n_metas--; - } - } - - gst_query_parse_allocation (query, &outcaps, NULL); - - /* we got configuration from our peer or the decide_allocation method, - * parse them */ - if (gst_query_get_n_allocation_params (query) > 0) { - /* try the allocator */ - gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶ms); - update_allocator = TRUE; - } else { - allocator = NULL; - gst_allocation_params_init (¶ms); - update_allocator = FALSE; - } - - if (gst_query_get_n_allocation_pools (query) > 0) { - gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); - - if (pool == NULL) { - /* no pool, we can make our own */ - GST_DEBUG_OBJECT (trans, "no pool, making new pool"); - pool = gst_buffer_pool_new (); - } - } else { - pool = NULL; - size = min = max = 0; - } - - /* now configure */ - if (pool) { - config = gst_buffer_pool_get_config (pool); - gst_buffer_pool_config_set_params (config, outcaps, size, min, max); - gst_buffer_pool_config_set_allocator (config, allocator, ¶ms); - - /* buffer pool may have to do some changes */ - if (!gst_buffer_pool_set_config (pool, config)) { - config = gst_buffer_pool_get_config (pool); - - /* If change are not acceptable, fallback to generic pool */ - if (!gst_buffer_pool_config_validate_params (config, outcaps, size, min, - max)) { - GST_DEBUG_OBJECT (trans, "unsupported pool, making new pool"); - - gst_object_unref (pool); - pool = gst_buffer_pool_new (); - gst_buffer_pool_config_set_params (config, outcaps, size, min, max); - gst_buffer_pool_config_set_allocator (config, allocator, ¶ms); - } - - if (!gst_buffer_pool_set_config (pool, config)) - goto config_failed; - } - } - - if (update_allocator) - gst_query_set_nth_allocation_param (query, 0, allocator, ¶ms); - else - gst_query_add_allocation_param (query, allocator, ¶ms); - if (allocator) - gst_object_unref (allocator); - - if (pool) { - gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max); - gst_object_unref (pool); - } - - return TRUE; - -config_failed: - if (pool) - gst_object_unref (pool); - - GST_ELEMENT_ERROR (trans, RESOURCE, SETTINGS, - ("Failed to configure the buffer pool"), - ("Configuration is most likely invalid, please report this issue.")); - return FALSE; -} - -static gboolean -gst_base_transform_do_bufferpool (GstBaseTransform * trans, GstCaps * outcaps) -{ - GstQuery *query; - gboolean result = TRUE; - GstBufferPool *pool = NULL; - GstBaseTransformClass *klass; - GstBaseTransformPrivate *priv = trans->priv; - GstAllocator *allocator; - GstAllocationParams params; - - /* there are these possibilities: - * - * 1) we negotiated passthrough, we can proxy the bufferpool directly and we - * will do that whenever some upstream does an allocation query. - * 2) we need to do a transform, we need to get a bufferpool from downstream - * and configure it. When upstream does the ALLOCATION query, the - * propose_allocation vmethod will be called and we will configure the - * upstream allocator with our proposed values then. - */ - if (priv->passthrough || priv->always_in_place) { - /* we are in passthrough, the input buffer is never copied and always passed - * along. We never allocate an output buffer on the srcpad. What we do is - * let the upstream element decide if it wants to use a bufferpool and - * then we will proxy the downstream pool */ - GST_DEBUG_OBJECT (trans, "we're passthough, delay bufferpool"); - gst_base_transform_set_allocation (trans, NULL, NULL, NULL, NULL); - return TRUE; - } - - /* not passthrough, we need to allocate */ - /* find a pool for the negotiated caps now */ - GST_DEBUG_OBJECT (trans, "doing allocation query"); - query = gst_query_new_allocation (outcaps, TRUE); - if (!gst_pad_peer_query (trans->srcpad, query)) { - /* not a problem, just debug a little */ - GST_DEBUG_OBJECT (trans, "peer ALLOCATION query failed"); - } - - klass = GST_BASE_TRANSFORM_GET_CLASS (trans); - - GST_DEBUG_OBJECT (trans, "calling decide_allocation"); - g_assert (klass->decide_allocation != NULL); - result = klass->decide_allocation (trans, query); - - GST_DEBUG_OBJECT (trans, "ALLOCATION (%d) params: %" GST_PTR_FORMAT, result, - query); - - if (!result) - goto no_decide_allocation; - - /* check again in case the sub-class have switch to passthrough/in-place - * after looking at the meta APIs */ - if (priv->passthrough || priv->always_in_place) { - GST_DEBUG_OBJECT (trans, "no doing passthrough, delay bufferpool"); - gst_base_transform_set_allocation (trans, NULL, NULL, NULL, NULL); - gst_query_unref (query); - return TRUE; - } - - /* we got configuration from our peer or the decide_allocation method, - * parse them */ - if (gst_query_get_n_allocation_params (query) > 0) { - gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶ms); - } else { - allocator = NULL; - gst_allocation_params_init (¶ms); - } - - if (gst_query_get_n_allocation_pools (query) > 0) - gst_query_parse_nth_allocation_pool (query, 0, &pool, NULL, NULL, NULL); - - /* now store */ - result = - gst_base_transform_set_allocation (trans, pool, allocator, ¶ms, - query); - - return result; - - /* Errors */ -no_decide_allocation: - { - GST_WARNING_OBJECT (trans, "Subclass failed to decide allocation"); - gst_query_unref (query); - - return result; - } -} - -/* function triggered when the in and out caps are negotiated and need - * to be configured in the subclass. */ -static gboolean -gst_base_transform_configure_caps (GstBaseTransform * trans, GstCaps * in, - GstCaps * out) -{ - gboolean ret = TRUE; - GstBaseTransformClass *klass; - GstBaseTransformPrivate *priv = trans->priv; - - klass = GST_BASE_TRANSFORM_GET_CLASS (trans); - - GST_DEBUG_OBJECT (trans, "in caps: %" GST_PTR_FORMAT, in); - GST_DEBUG_OBJECT (trans, "out caps: %" GST_PTR_FORMAT, out); - - /* clear the cache */ - gst_caps_replace (&priv->cache_caps1, NULL); - gst_caps_replace (&priv->cache_caps2, NULL); - - /* figure out same caps state */ - priv->have_same_caps = gst_caps_is_equal (in, out); - GST_DEBUG_OBJECT (trans, "have_same_caps: %d", priv->have_same_caps); - - /* Set the passthrough if the class wants passthrough_on_same_caps - * and we have the same caps on each pad */ - if (klass->passthrough_on_same_caps) - gst_base_transform_set_passthrough (trans, priv->have_same_caps); - - /* now configure the element with the caps */ - if (klass->set_caps) { - GST_DEBUG_OBJECT (trans, "Calling set_caps method to setup caps"); - ret = klass->set_caps (trans, in, out); - } - - return ret; -} - -static GstCaps * -gst_base_transform_default_fixate_caps (GstBaseTransform * trans, - GstPadDirection direction, GstCaps * caps, GstCaps * othercaps) -{ - othercaps = gst_caps_fixate (othercaps); - GST_DEBUG_OBJECT (trans, "fixated to %" GST_PTR_FORMAT, othercaps); - - return othercaps; -} - -/* given a fixed @caps on @pad, create the best possible caps for the - * other pad. - * @caps must be fixed when calling this function. - * - * This function calls the transform caps vmethod of the basetransform to figure - * out the possible target formats. It then tries to select the best format from - * this list by: - * - * - attempt passthrough if the target caps is a superset of the input caps - * - fixating by using peer caps - * - fixating with transform fixate function - * - fixating with pad fixate functions. - * - * this function returns a caps that can be transformed into and is accepted by - * the peer element. - */ -static GstCaps * -gst_base_transform_find_transform (GstBaseTransform * trans, GstPad * pad, - GstCaps * caps) -{ - GstBaseTransformClass *klass; - GstPad *otherpad, *otherpeer; - GstCaps *othercaps; - gboolean is_fixed; - - /* caps must be fixed here, this is a programming error if it's not */ - g_return_val_if_fail (gst_caps_is_fixed (caps), NULL); - - klass = GST_BASE_TRANSFORM_GET_CLASS (trans); - - otherpad = (pad == trans->srcpad) ? trans->sinkpad : trans->srcpad; - otherpeer = gst_pad_get_peer (otherpad); - - /* see how we can transform the input caps. We need to do this even for - * passthrough because it might be possible that this element cannot support - * passthrough at all. */ - othercaps = gst_base_transform_transform_caps (trans, - GST_PAD_DIRECTION (pad), caps, NULL); - - /* The caps we can actually output is the intersection of the transformed - * caps with the pad template for the pad */ - if (othercaps && !gst_caps_is_empty (othercaps)) { - GstCaps *intersect, *templ_caps; - - templ_caps = gst_pad_get_pad_template_caps (otherpad); - GST_DEBUG_OBJECT (trans, - "intersecting against padtemplate %" GST_PTR_FORMAT, templ_caps); - - intersect = - gst_caps_intersect_full (othercaps, templ_caps, - GST_CAPS_INTERSECT_FIRST); - - gst_caps_unref (othercaps); - gst_caps_unref (templ_caps); - othercaps = intersect; - } - - /* check if transform is empty */ - if (!othercaps || gst_caps_is_empty (othercaps)) - goto no_transform; - - /* if the othercaps are not fixed, we need to fixate them, first attempt - * is by attempting passthrough if the othercaps are a superset of caps. */ - /* FIXME. maybe the caps is not fixed because it has multiple structures of - * fixed caps */ - is_fixed = gst_caps_is_fixed (othercaps); - if (!is_fixed) { - GST_DEBUG_OBJECT (trans, - "transform returned non fixed %" GST_PTR_FORMAT, othercaps); - - /* Now let's see what the peer suggests based on our transformed caps */ - if (otherpeer) { - GstCaps *peercaps, *intersection, *templ_caps; - - GST_DEBUG_OBJECT (trans, - "Checking peer caps with filter %" GST_PTR_FORMAT, othercaps); - - peercaps = gst_pad_query_caps (otherpeer, othercaps); - GST_DEBUG_OBJECT (trans, "Resulted in %" GST_PTR_FORMAT, peercaps); - if (!gst_caps_is_empty (peercaps)) { - templ_caps = gst_pad_get_pad_template_caps (otherpad); - - GST_DEBUG_OBJECT (trans, - "Intersecting with template caps %" GST_PTR_FORMAT, templ_caps); - - intersection = - gst_caps_intersect_full (peercaps, templ_caps, - GST_CAPS_INTERSECT_FIRST); - GST_DEBUG_OBJECT (trans, "Intersection: %" GST_PTR_FORMAT, - intersection); - gst_caps_unref (peercaps); - gst_caps_unref (templ_caps); - peercaps = intersection; - - GST_DEBUG_OBJECT (trans, - "Intersecting with transformed caps %" GST_PTR_FORMAT, othercaps); - intersection = - gst_caps_intersect_full (peercaps, othercaps, - GST_CAPS_INTERSECT_FIRST); - GST_DEBUG_OBJECT (trans, "Intersection: %" GST_PTR_FORMAT, - intersection); - gst_caps_unref (peercaps); - gst_caps_unref (othercaps); - othercaps = intersection; - } else { - gst_caps_unref (othercaps); - othercaps = peercaps; - } - - is_fixed = gst_caps_is_fixed (othercaps); - } else { - GST_DEBUG_OBJECT (trans, "no peer, doing passthrough"); - gst_caps_unref (othercaps); - othercaps = gst_caps_ref (caps); - is_fixed = TRUE; - } - } - if (gst_caps_is_empty (othercaps)) - goto no_transform_possible; - - GST_DEBUG ("have %sfixed caps %" GST_PTR_FORMAT, (is_fixed ? "" : "non-"), - othercaps); - - /* second attempt at fixation, call the fixate vmethod */ - /* caps could be fixed but the subclass may want to add fields */ - if (klass->fixate_caps) { - GST_DEBUG_OBJECT (trans, "calling fixate_caps for %" GST_PTR_FORMAT - " using caps %" GST_PTR_FORMAT " on pad %s:%s", othercaps, caps, - GST_DEBUG_PAD_NAME (otherpad)); - /* note that we pass the complete array of structures to the fixate - * function, it needs to truncate itself */ - othercaps = - klass->fixate_caps (trans, GST_PAD_DIRECTION (pad), caps, othercaps); - - if (!othercaps) { - g_critical ("basetransform: second attempt to fixate caps returned " - "invalid (NULL) caps on pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - } - is_fixed = othercaps && gst_caps_is_fixed (othercaps); - GST_DEBUG_OBJECT (trans, "after fixating %" GST_PTR_FORMAT, othercaps); - } - - /* caps should be fixed now, if not we have to fail. */ - if (!is_fixed) - goto could_not_fixate; - - /* and peer should accept */ - if (otherpeer && !gst_pad_query_accept_caps (otherpeer, othercaps)) - goto peer_no_accept; - - GST_DEBUG_OBJECT (trans, "Input caps were %" GST_PTR_FORMAT - ", and got final caps %" GST_PTR_FORMAT, caps, othercaps); - - if (otherpeer) - gst_object_unref (otherpeer); - - return othercaps; - - /* ERRORS */ -no_transform: - { - GST_DEBUG_OBJECT (trans, - "transform returned useless %" GST_PTR_FORMAT, othercaps); - goto error_cleanup; - } -no_transform_possible: - { - GST_DEBUG_OBJECT (trans, - "transform could not transform %" GST_PTR_FORMAT - " in anything we support", caps); - goto error_cleanup; - } -could_not_fixate: - { - GST_DEBUG_OBJECT (trans, "FAILED to fixate %" GST_PTR_FORMAT, othercaps); - goto error_cleanup; - } -peer_no_accept: - { - GST_DEBUG_OBJECT (trans, "FAILED to get peer of %" GST_PTR_FORMAT - " to accept %" GST_PTR_FORMAT, otherpad, othercaps); - goto error_cleanup; - } -error_cleanup: - { - if (otherpeer) - gst_object_unref (otherpeer); - if (othercaps) - gst_caps_unref (othercaps); - return NULL; - } -} - -static gboolean -gst_base_transform_acceptcaps_default (GstBaseTransform * trans, - GstPadDirection direction, GstCaps * caps) -{ - GstPad *pad, *otherpad; - GstCaps *templ, *otempl, *ocaps = NULL; - gboolean ret = TRUE; - - pad = - (direction == - GST_PAD_SINK) ? GST_BASE_TRANSFORM_SINK_PAD (trans) : - GST_BASE_TRANSFORM_SRC_PAD (trans); - otherpad = - (direction == - GST_PAD_SINK) ? GST_BASE_TRANSFORM_SRC_PAD (trans) : - GST_BASE_TRANSFORM_SINK_PAD (trans); - - GST_DEBUG_OBJECT (trans, "accept caps %" GST_PTR_FORMAT, caps); - - templ = gst_pad_get_pad_template_caps (pad); - otempl = gst_pad_get_pad_template_caps (otherpad); - - /* get all the formats we can handle on this pad */ - GST_DEBUG_OBJECT (trans, "intersect with pad template: %" GST_PTR_FORMAT, - templ); - if (!gst_caps_can_intersect (caps, templ)) - goto reject_caps; - - GST_DEBUG_OBJECT (trans, "trying to transform with filter: %" - GST_PTR_FORMAT " (the other pad template)", otempl); - ocaps = gst_base_transform_transform_caps (trans, direction, caps, otempl); - if (!ocaps || gst_caps_is_empty (ocaps)) - goto no_transform_possible; - -done: - GST_DEBUG_OBJECT (trans, "accept-caps result: %d", ret); - if (ocaps) - gst_caps_unref (ocaps); - gst_caps_unref (templ); - gst_caps_unref (otempl); - return ret; - - /* ERRORS */ -reject_caps: - { - GST_DEBUG_OBJECT (trans, "caps can't intersect with the template"); - ret = FALSE; - goto done; - } -no_transform_possible: - { - GST_DEBUG_OBJECT (trans, - "transform could not transform %" GST_PTR_FORMAT - " in anything we support", caps); - ret = FALSE; - goto done; - } -} - -/* called when new caps arrive on the sink pad, - * We try to find the best caps for the other side using our _find_transform() - * function. If there are caps, we configure the transform for this new - * transformation. - */ -static gboolean -gst_base_transform_setcaps (GstBaseTransform * trans, GstPad * pad, - GstCaps * incaps) -{ - GstBaseTransformPrivate *priv = trans->priv; - GstCaps *outcaps, *prev_incaps = NULL, *prev_outcaps = NULL; - gboolean ret = TRUE; - - GST_DEBUG_OBJECT (pad, "have new caps %p %" GST_PTR_FORMAT, incaps, incaps); - - /* find best possible caps for the other pad */ - outcaps = gst_base_transform_find_transform (trans, pad, incaps); - if (!outcaps || gst_caps_is_empty (outcaps)) - goto no_transform_possible; - - /* configure the element now */ - - /* if we have the same caps, we can optimize and reuse the input caps */ - if (gst_caps_is_equal (incaps, outcaps)) { - GST_INFO_OBJECT (trans, "reuse caps"); - gst_caps_unref (outcaps); - outcaps = gst_caps_ref (incaps); - } - - prev_incaps = gst_pad_get_current_caps (trans->sinkpad); - prev_outcaps = gst_pad_get_current_caps (trans->srcpad); - if (prev_incaps && prev_outcaps && gst_caps_is_equal (prev_incaps, incaps) - && gst_caps_is_equal (prev_outcaps, outcaps)) { - GST_DEBUG_OBJECT (trans, - "New caps equal to old ones: %" GST_PTR_FORMAT " -> %" GST_PTR_FORMAT, - incaps, outcaps); - ret = TRUE; - } else { - /* call configure now */ - if (!(ret = gst_base_transform_configure_caps (trans, incaps, outcaps))) - goto failed_configure; - - if (!prev_outcaps || !gst_caps_is_equal (outcaps, prev_outcaps)) - /* let downstream know about our caps */ - ret = gst_pad_set_caps (trans->srcpad, outcaps); - } - - if (ret) { - /* try to get a pool when needed */ - ret = gst_base_transform_do_bufferpool (trans, outcaps); - } - -done: - if (outcaps) - gst_caps_unref (outcaps); - if (prev_incaps) - gst_caps_unref (prev_incaps); - if (prev_outcaps) - gst_caps_unref (prev_outcaps); - - GST_OBJECT_LOCK (trans); - priv->negotiated = ret; - GST_OBJECT_UNLOCK (trans); - - return ret; - - /* ERRORS */ -no_transform_possible: - { - GST_WARNING_OBJECT (trans, - "transform could not transform %" GST_PTR_FORMAT - " in anything we support", incaps); - ret = FALSE; - goto done; - } -failed_configure: - { - GST_WARNING_OBJECT (trans, "FAILED to configure incaps %" GST_PTR_FORMAT - " and outcaps %" GST_PTR_FORMAT, incaps, outcaps); - ret = FALSE; - goto done; - } -} - -static gboolean -gst_base_transform_default_propose_allocation (GstBaseTransform * trans, - GstQuery * decide_query, GstQuery * query) -{ - gboolean ret; - - if (decide_query == NULL) { - GST_DEBUG_OBJECT (trans, "doing passthrough query"); - ret = gst_pad_peer_query (trans->srcpad, query); - } else { - guint i, n_metas; - /* non-passthrough, copy all metadata, decide_query does not contain the - * metadata anymore that depends on the buffer memory */ - n_metas = gst_query_get_n_allocation_metas (decide_query); - for (i = 0; i < n_metas; i++) { - GType api; - const GstStructure *params; - - api = gst_query_parse_nth_allocation_meta (decide_query, i, ¶ms); - GST_DEBUG_OBJECT (trans, "proposing metadata %s", g_type_name (api)); - gst_query_add_allocation_meta (query, api, params); - } - ret = TRUE; - } - return ret; -} - -static gboolean -gst_base_transform_reconfigure_unlocked (GstBaseTransform * trans) -{ - gboolean reconfigure, ret = TRUE; - - reconfigure = gst_pad_check_reconfigure (trans->srcpad); - - if (G_UNLIKELY (reconfigure)) { - GstCaps *incaps; - - GST_DEBUG_OBJECT (trans, "we had a pending reconfigure"); - - incaps = gst_pad_get_current_caps (trans->sinkpad); - if (incaps == NULL) - goto done; - - /* if we need to reconfigure we pretend new caps arrived. This - * will reconfigure the transform with the new output format. */ - if (!gst_base_transform_setcaps (trans, trans->sinkpad, incaps)) { - GST_ELEMENT_WARNING (trans, STREAM, FORMAT, - ("not negotiated"), ("not negotiated")); - ret = FALSE; - } - - gst_caps_unref (incaps); - } - -done: - - if (!ret) - gst_pad_mark_reconfigure (trans->srcpad); - - return ret; -} - -/** - * gst_base_transform_reconfigure: - * @trans: the #GstBaseTransform to set - * - * Negotiates src pad caps with downstream elements if the source pad is - * marked as needing reconfiguring. Unmarks GST_PAD_FLAG_NEED_RECONFIGURE in - * any case. But marks it again if negotiation fails. - * - * Do not call this in the #GstBaseTransformClass::transform or - * #GstBaseTransformClass::transform_ip vmethod. Call this in - * #GstBaseTransformClass::submit_input_buffer, - * #GstBaseTransformClass::prepare_output_buffer or in - * #GstBaseTransformClass::generate_output _before_ any output buffer is - * allocated. - * - * It will be default be called when handling an ALLOCATION query or at the - * very beginning of the default #GstBaseTransformClass::submit_input_buffer - * implementation. - * - * Returns: %TRUE if the negotiation succeeded, else %FALSE. - * - * Since: 1.18 - */ -gboolean -gst_base_transform_reconfigure (GstBaseTransform * trans) -{ - gboolean ret; - - g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), FALSE); - - GST_PAD_STREAM_LOCK (trans->sinkpad); - ret = gst_base_transform_reconfigure_unlocked (trans); - if (!ret) - gst_pad_mark_reconfigure (trans->srcpad); - GST_PAD_STREAM_UNLOCK (trans->sinkpad); - - return ret; -} - -static gboolean -gst_base_transform_default_query (GstBaseTransform * trans, - GstPadDirection direction, GstQuery * query) -{ - gboolean ret = FALSE; - GstPad *pad, *otherpad; - GstBaseTransformClass *klass; - GstBaseTransformPrivate *priv = trans->priv; - - if (direction == GST_PAD_SRC) { - pad = trans->srcpad; - otherpad = trans->sinkpad; - } else { - pad = trans->sinkpad; - otherpad = trans->srcpad; - } - - klass = GST_BASE_TRANSFORM_GET_CLASS (trans); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_ALLOCATION: - { - GstQuery *decide_query = NULL; - - /* can only be done on the sinkpad */ - if (direction != GST_PAD_SINK) - goto done; - - ret = gst_base_transform_reconfigure_unlocked (trans); - if (G_UNLIKELY (!ret)) - goto done; - - GST_OBJECT_LOCK (trans); - if (!priv->negotiated && !priv->passthrough && (klass->set_caps != NULL)) { - GST_DEBUG_OBJECT (trans, - "not negotiated yet but need negotiation, can't answer ALLOCATION query"); - GST_OBJECT_UNLOCK (trans); - goto done; - } - - decide_query = trans->priv->query; - trans->priv->query = NULL; - GST_OBJECT_UNLOCK (trans); - - GST_DEBUG_OBJECT (trans, - "calling propose allocation with query %" GST_PTR_FORMAT, - decide_query); - - /* pass the query to the propose_allocation vmethod if any */ - if (G_LIKELY (klass->propose_allocation)) - ret = klass->propose_allocation (trans, decide_query, query); - else - ret = FALSE; - - if (decide_query) { - GST_OBJECT_LOCK (trans); - - if (trans->priv->query == NULL) - trans->priv->query = decide_query; - else - gst_query_unref (decide_query); - - GST_OBJECT_UNLOCK (trans); - } - - GST_DEBUG_OBJECT (trans, "ALLOCATION ret %d, %" GST_PTR_FORMAT, ret, - query); - break; - } - case GST_QUERY_POSITION: - { - GstFormat format; - - gst_query_parse_position (query, &format, NULL); - if (format == GST_FORMAT_TIME && trans->segment.format == GST_FORMAT_TIME) { - gint64 pos; - ret = TRUE; - - if ((direction == GST_PAD_SINK) - || (trans->priv->position_out == GST_CLOCK_TIME_NONE)) { - pos = - gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, - trans->segment.position); - } else { - pos = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, - trans->priv->position_out); - } - gst_query_set_position (query, format, pos); - } else { - ret = gst_pad_peer_query (otherpad, query); - } - break; - } - case GST_QUERY_ACCEPT_CAPS: - { - GstCaps *caps; - - gst_query_parse_accept_caps (query, &caps); - if (klass->accept_caps) { - ret = klass->accept_caps (trans, direction, caps); - gst_query_set_accept_caps_result (query, ret); - /* return TRUE, we answered the query */ - ret = TRUE; - } - break; - } - case GST_QUERY_CAPS: - { - GstCaps *filter, *caps; - - gst_query_parse_caps (query, &filter); - caps = gst_base_transform_query_caps (trans, pad, filter); - gst_query_set_caps_result (query, caps); - gst_caps_unref (caps); - ret = TRUE; - break; - } - default: - ret = gst_pad_peer_query (otherpad, query); - break; - } - -done: - return ret; -} - -static gboolean -gst_base_transform_query (GstPad * pad, GstObject * parent, GstQuery * query) -{ - GstBaseTransform *trans; - GstBaseTransformClass *bclass; - gboolean ret = FALSE; - - trans = GST_BASE_TRANSFORM_CAST (parent); - bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); - - if (bclass->query) - ret = bclass->query (trans, GST_PAD_DIRECTION (pad), query); - - return ret; -} - -/* this function either returns the input buffer without incrementing the - * refcount or it allocates a new (writable) buffer */ -static GstFlowReturn -default_prepare_output_buffer (GstBaseTransform * trans, - GstBuffer * inbuf, GstBuffer ** outbuf) -{ - GstBaseTransformPrivate *priv; - GstFlowReturn ret; - GstBaseTransformClass *bclass; - GstCaps *incaps, *outcaps; - gsize insize, outsize; - gboolean res; - - priv = trans->priv; - bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); - - /* figure out how to allocate an output buffer */ - if (priv->passthrough) { - /* passthrough, we will not modify the incoming buffer so we can just - * reuse it */ - GST_DEBUG_OBJECT (trans, "passthrough: reusing input buffer"); - *outbuf = inbuf; - goto done; - } - - /* we can't reuse the input buffer */ - if (priv->pool) { - if (!priv->pool_active) { - GST_DEBUG_OBJECT (trans, "setting pool %p active", priv->pool); - if (!gst_buffer_pool_set_active (priv->pool, TRUE)) - goto activate_failed; - priv->pool_active = TRUE; - } - GST_DEBUG_OBJECT (trans, "using pool alloc"); - ret = gst_buffer_pool_acquire_buffer (priv->pool, outbuf, NULL); - if (ret != GST_FLOW_OK) - goto alloc_failed; - - goto copy_meta; - } - - /* no pool, we need to figure out the size of the output buffer first */ - if ((bclass->transform_ip != NULL) && priv->always_in_place) { - /* we want to do an in-place alloc */ - if (gst_buffer_is_writable (inbuf)) { - GST_DEBUG_OBJECT (trans, "inplace reuse writable input buffer"); - *outbuf = inbuf; - } else { - GST_DEBUG_OBJECT (trans, "making writable buffer copy"); - /* we make a copy of the input buffer */ - *outbuf = gst_buffer_copy (inbuf); - } - goto done; - } - - /* else use the transform function to get the size */ - incaps = gst_pad_get_current_caps (trans->sinkpad); - outcaps = gst_pad_get_current_caps (trans->srcpad); - - /* srcpad might be flushing already if we're being shut down */ - if (outcaps == NULL) - goto no_outcaps; - - GST_DEBUG_OBJECT (trans, "getting output size for alloc"); - /* copy transform, figure out the output size */ - insize = gst_buffer_get_size (inbuf); - res = gst_base_transform_transform_size (trans, - GST_PAD_SINK, incaps, insize, outcaps, &outsize); - - gst_caps_unref (incaps); - gst_caps_unref (outcaps); - - if (!res) - goto unknown_size; - - GST_DEBUG_OBJECT (trans, "doing alloc of size %" G_GSIZE_FORMAT, outsize); - *outbuf = gst_buffer_new_allocate (priv->allocator, outsize, &priv->params); - if (!*outbuf) { - ret = GST_FLOW_ERROR; - goto alloc_failed; - } - -copy_meta: - /* copy the metadata */ - if (bclass->copy_metadata) - if (!bclass->copy_metadata (trans, inbuf, *outbuf)) { - /* something failed, post a warning */ - GST_ELEMENT_WARNING (trans, STREAM, NOT_IMPLEMENTED, - ("could not copy metadata"), (NULL)); - } - -done: - return GST_FLOW_OK; - - /* ERRORS */ -activate_failed: - { - GST_ELEMENT_ERROR (trans, RESOURCE, SETTINGS, - ("failed to activate bufferpool"), ("failed to activate bufferpool")); - return GST_FLOW_ERROR; - } -unknown_size: - { - GST_ERROR_OBJECT (trans, "unknown output size"); - return GST_FLOW_ERROR; - } -alloc_failed: - { - GST_DEBUG_OBJECT (trans, "could not allocate buffer from pool"); - return ret; - } -no_outcaps: - { - GST_DEBUG_OBJECT (trans, "no output caps, source pad has been deactivated"); - gst_caps_unref (incaps); - return GST_FLOW_FLUSHING; - } -} - -typedef struct -{ - GstBaseTransform *trans; - GstBuffer *outbuf; -} CopyMetaData; - -static gboolean -foreach_metadata (GstBuffer * inbuf, GstMeta ** meta, gpointer user_data) -{ - CopyMetaData *data = user_data; - GstBaseTransform *trans = data->trans; - GstBaseTransformClass *klass; - const GstMetaInfo *info = (*meta)->info; - GstBuffer *outbuf = data->outbuf; - gboolean do_copy = FALSE; - - klass = GST_BASE_TRANSFORM_GET_CLASS (trans); - - if (gst_meta_api_type_has_tag (info->api, _gst_meta_tag_memory)) { - /* never call the transform_meta with memory specific metadata */ - GST_DEBUG_OBJECT (trans, "not copying memory specific metadata %s", - g_type_name (info->api)); - do_copy = FALSE; - } else if (klass->transform_meta) { - do_copy = klass->transform_meta (trans, outbuf, *meta, inbuf); - GST_DEBUG_OBJECT (trans, "transformed metadata %s: copy: %d", - g_type_name (info->api), do_copy); - } - - /* we only copy metadata when the subclass implemented a transform_meta - * function and when it returns %TRUE */ - if (do_copy) { - GstMetaTransformCopy copy_data = { FALSE, 0, -1 }; - /* simply copy then */ - if (info->transform_func) { - GST_DEBUG_OBJECT (trans, "copy metadata %s", g_type_name (info->api)); - info->transform_func (outbuf, *meta, inbuf, - _gst_meta_transform_copy, ©_data); - } else { - GST_DEBUG_OBJECT (trans, "couldn't copy metadata %s", - g_type_name (info->api)); - } - } - return TRUE; -} - -static gboolean -default_copy_metadata (GstBaseTransform * trans, - GstBuffer * inbuf, GstBuffer * outbuf) -{ - GstBaseTransformPrivate *priv = trans->priv; - CopyMetaData data; - - /* now copy the metadata */ - GST_DEBUG_OBJECT (trans, "copying metadata"); - - /* this should not happen, buffers allocated from a pool or with - * new_allocate should always be writable. */ - if (!gst_buffer_is_writable (outbuf)) - goto not_writable; - - /* when we get here, the metadata should be writable */ - gst_buffer_copy_into (outbuf, inbuf, - GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1); - - /* clear the GAP flag when the subclass does not understand it */ - if (!priv->gap_aware) - GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_GAP); - - - data.trans = trans; - data.outbuf = outbuf; - - gst_buffer_foreach_meta (inbuf, foreach_metadata, &data); - - return TRUE; - - /* ERRORS */ -not_writable: - { - GST_WARNING_OBJECT (trans, "buffer %p not writable", outbuf); - return FALSE; - } -} - -/* Given @caps calculate the size of one unit. - * - * For video caps, this is the size of one frame (and thus one buffer). - * For audio caps, this is the size of one sample. - * - * These values are cached since they do not change and the calculation - * potentially involves parsing caps and other expensive stuff. - * - * We have two cache locations to store the size, one for the source caps - * and one for the sink caps. - * - * this function returns %FALSE if no size could be calculated. - */ -static gboolean -gst_base_transform_get_unit_size (GstBaseTransform * trans, GstCaps * caps, - gsize * size) -{ - gboolean res = FALSE; - GstBaseTransformClass *bclass; - GstBaseTransformPrivate *priv = trans->priv; - - /* see if we have the result cached */ - if (priv->cache_caps1 == caps) { - *size = priv->cache_caps1_size; - GST_DEBUG_OBJECT (trans, - "returned %" G_GSIZE_FORMAT " from first cache", *size); - return TRUE; - } - if (priv->cache_caps2 == caps) { - *size = priv->cache_caps2_size; - GST_DEBUG_OBJECT (trans, - "returned %" G_GSIZE_FORMAT " from second cached", *size); - return TRUE; - } - - bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); - res = bclass->get_unit_size (trans, caps, size); - GST_DEBUG_OBJECT (trans, - "caps %" GST_PTR_FORMAT " has unit size %" G_GSIZE_FORMAT ", res %s", - caps, *size, res ? "TRUE" : "FALSE"); - - if (res) { - /* and cache the values */ - if (priv->cache_caps1 == NULL) { - gst_caps_replace (&priv->cache_caps1, caps); - priv->cache_caps1_size = *size; - GST_DEBUG_OBJECT (trans, - "caching %" G_GSIZE_FORMAT " in first cache", *size); - } else if (priv->cache_caps2 == NULL) { - gst_caps_replace (&priv->cache_caps2, caps); - priv->cache_caps2_size = *size; - GST_DEBUG_OBJECT (trans, - "caching %" G_GSIZE_FORMAT " in second cache", *size); - } else { - GST_DEBUG_OBJECT (trans, "no free spot to cache unit_size"); - } - } - return res; -} - -static gboolean -gst_base_transform_sink_event (GstPad * pad, GstObject * parent, - GstEvent * event) -{ - GstBaseTransform *trans; - GstBaseTransformClass *bclass; - gboolean ret = TRUE; - - trans = GST_BASE_TRANSFORM_CAST (parent); - bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); - - if (bclass->sink_event) - ret = bclass->sink_event (trans, event); - else - gst_event_unref (event); - - return ret; -} - -static gboolean -gst_base_transform_sink_eventfunc (GstBaseTransform * trans, GstEvent * event) -{ - gboolean ret = TRUE, forward = TRUE; - GstBaseTransformPrivate *priv = trans->priv; - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_START: - break; - case GST_EVENT_FLUSH_STOP: - GST_OBJECT_LOCK (trans); - /* reset QoS parameters */ - priv->proportion = 1.0; - priv->earliest_time = -1; - priv->discont = FALSE; - priv->processed = 0; - priv->dropped = 0; - GST_OBJECT_UNLOCK (trans); - /* we need new segment info after the flush. */ - trans->have_segment = FALSE; - gst_segment_init (&trans->segment, GST_FORMAT_UNDEFINED); - priv->position_out = GST_CLOCK_TIME_NONE; - break; - case GST_EVENT_EOS: - break; - case GST_EVENT_TAG: - break; - case GST_EVENT_CAPS: - { - GstCaps *caps; - - gst_event_parse_caps (event, &caps); - /* clear any pending reconfigure flag */ - gst_pad_check_reconfigure (trans->srcpad); - ret = gst_base_transform_setcaps (trans, trans->sinkpad, caps); - if (!ret) - gst_pad_mark_reconfigure (trans->srcpad); - - forward = FALSE; - break; - } - case GST_EVENT_SEGMENT: - { - gst_event_copy_segment (event, &trans->segment); - trans->have_segment = TRUE; - - GST_DEBUG_OBJECT (trans, "received SEGMENT %" GST_SEGMENT_FORMAT, - &trans->segment); - break; - } - default: - break; - } - - if (ret && forward) - ret = gst_pad_push_event (trans->srcpad, event); - else - gst_event_unref (event); - - return ret; -} - -static gboolean -gst_base_transform_src_event (GstPad * pad, GstObject * parent, - GstEvent * event) -{ - GstBaseTransform *trans; - GstBaseTransformClass *bclass; - gboolean ret = TRUE; - - trans = GST_BASE_TRANSFORM_CAST (parent); - bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); - - if (bclass->src_event) - ret = bclass->src_event (trans, event); - else - gst_event_unref (event); - - return ret; -} - -static gboolean -gst_base_transform_src_eventfunc (GstBaseTransform * trans, GstEvent * event) -{ - gboolean ret; - - GST_DEBUG_OBJECT (trans, "handling event %p %" GST_PTR_FORMAT, event, event); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - break; - case GST_EVENT_NAVIGATION: - break; - case GST_EVENT_QOS: - { - gdouble proportion; - GstClockTimeDiff diff; - GstClockTime timestamp; - - gst_event_parse_qos (event, NULL, &proportion, &diff, ×tamp); - gst_base_transform_update_qos (trans, proportion, diff, timestamp); - break; - } - default: - break; - } - - ret = gst_pad_push_event (trans->sinkpad, event); - - return ret; -} - -/* Takes the input buffer */ -static GstFlowReturn -default_submit_input_buffer (GstBaseTransform * trans, gboolean is_discont, - GstBuffer * inbuf) -{ - GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); - GstBaseTransformPrivate *priv = trans->priv; - GstFlowReturn ret = GST_FLOW_OK; - GstClockTime running_time; - GstClockTime timestamp; - - if (G_UNLIKELY (!gst_base_transform_reconfigure_unlocked (trans))) - goto not_negotiated; - - if (GST_BUFFER_OFFSET_IS_VALID (inbuf)) - GST_DEBUG_OBJECT (trans, - "handling buffer %p of size %" G_GSIZE_FORMAT ", PTS %" GST_TIME_FORMAT - " and offset %" G_GUINT64_FORMAT, inbuf, gst_buffer_get_size (inbuf), - GST_TIME_ARGS (GST_BUFFER_PTS (inbuf)), GST_BUFFER_OFFSET (inbuf)); - else - GST_DEBUG_OBJECT (trans, - "handling buffer %p of size %" G_GSIZE_FORMAT ", PTS %" GST_TIME_FORMAT - " and offset NONE", inbuf, gst_buffer_get_size (inbuf), - GST_TIME_ARGS (GST_BUFFER_PTS (inbuf))); - - /* Don't allow buffer handling before negotiation, except in passthrough mode - * or if the class doesn't implement a set_caps function (in which case it doesn't - * care about caps) - */ - if (!priv->negotiated && !priv->passthrough && (bclass->set_caps != NULL)) - goto not_negotiated; - - /* can only do QoS if the segment is in TIME */ - if (trans->segment.format != GST_FORMAT_TIME) - goto no_qos; - - /* QOS is done on the running time of the buffer, get it now */ - timestamp = GST_BUFFER_TIMESTAMP (inbuf); - running_time = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME, - timestamp); - - if (running_time != -1) { - gboolean need_skip; - GstClockTime earliest_time; - gdouble proportion; - - /* lock for getting the QoS parameters that are set (in a different thread) - * with the QOS events */ - GST_OBJECT_LOCK (trans); - earliest_time = priv->earliest_time; - proportion = priv->proportion; - /* check for QoS, don't perform conversion for buffers - * that are known to be late. */ - need_skip = priv->qos_enabled && - earliest_time != -1 && running_time <= earliest_time; - GST_OBJECT_UNLOCK (trans); - - if (need_skip) { - GstMessage *qos_msg; - GstClockTime duration; - guint64 stream_time; - gint64 jitter; - - GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, trans, "skipping transform: qostime %" - GST_TIME_FORMAT " <= %" GST_TIME_FORMAT, - GST_TIME_ARGS (running_time), GST_TIME_ARGS (earliest_time)); - - priv->dropped++; - - duration = GST_BUFFER_DURATION (inbuf); - stream_time = - gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME, - timestamp); - jitter = GST_CLOCK_DIFF (running_time, earliest_time); - - qos_msg = - gst_message_new_qos (GST_OBJECT_CAST (trans), FALSE, running_time, - stream_time, timestamp, duration); - gst_message_set_qos_values (qos_msg, jitter, proportion, 1000000); - gst_message_set_qos_stats (qos_msg, GST_FORMAT_BUFFERS, - priv->processed, priv->dropped); - gst_element_post_message (GST_ELEMENT_CAST (trans), qos_msg); - - /* mark discont for next buffer */ - priv->discont = TRUE; - ret = GST_BASE_TRANSFORM_FLOW_DROPPED; - goto skip; - } - } - -no_qos: - /* Stash input buffer where the default generate_output - * function can find it */ - if (trans->queued_buf) - gst_buffer_unref (trans->queued_buf); - trans->queued_buf = inbuf; - return ret; -skip: - gst_buffer_unref (inbuf); - return ret; - -not_negotiated: - { - gst_buffer_unref (inbuf); - if (GST_PAD_IS_FLUSHING (trans->srcpad)) - return GST_FLOW_FLUSHING; - return GST_FLOW_NOT_NEGOTIATED; - } -} - -static GstFlowReturn -default_generate_output (GstBaseTransform * trans, GstBuffer ** outbuf) -{ - GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); - GstBaseTransformPrivate *priv = trans->priv; - GstFlowReturn ret = GST_FLOW_OK; - GstBuffer *inbuf; - gboolean want_in_place; - - /* Retrieve stashed input buffer, if the default submit_input_buffer - * was run. Takes ownership back from there */ - inbuf = trans->queued_buf; - trans->queued_buf = NULL; - - /* This default processing method needs one input buffer to feed to - * the transform functions, we can't do anything without it */ - if (inbuf == NULL) - return GST_FLOW_OK; - - /* first try to allocate an output buffer based on the currently negotiated - * format. outbuf will contain a buffer suitable for doing the configured - * transform after this function. */ - if (bclass->prepare_output_buffer == NULL) - goto no_prepare; - - GST_DEBUG_OBJECT (trans, "calling prepare buffer"); - ret = bclass->prepare_output_buffer (trans, inbuf, outbuf); - - if (ret != GST_FLOW_OK || *outbuf == NULL) - goto no_buffer; - - GST_DEBUG_OBJECT (trans, "using allocated buffer in %p, out %p", inbuf, - *outbuf); - - /* now perform the needed transform */ - if (priv->passthrough) { - /* In passthrough mode, give transform_ip a look at the - * buffer, without making it writable, or just push the - * data through */ - if (bclass->transform_ip_on_passthrough && bclass->transform_ip) { - GST_DEBUG_OBJECT (trans, "doing passthrough transform_ip"); - ret = bclass->transform_ip (trans, *outbuf); - } else { - GST_DEBUG_OBJECT (trans, "element is in passthrough"); - } - } else { - want_in_place = (bclass->transform_ip != NULL) && priv->always_in_place; - - if (want_in_place) { - GST_DEBUG_OBJECT (trans, "doing inplace transform"); - ret = bclass->transform_ip (trans, *outbuf); - } else { - GST_DEBUG_OBJECT (trans, "doing non-inplace transform"); - - if (bclass->transform) - ret = bclass->transform (trans, inbuf, *outbuf); - else - ret = GST_FLOW_NOT_SUPPORTED; - } - } - - /* only unref input buffer if we allocated a new outbuf buffer. If we reused - * the input buffer, no refcount is changed to keep the input buffer writable - * when needed. */ - if (*outbuf != inbuf) - gst_buffer_unref (inbuf); - - return ret; - - /* ERRORS */ -no_prepare: - { - gst_buffer_unref (inbuf); - GST_ELEMENT_ERROR (trans, STREAM, NOT_IMPLEMENTED, - ("Sub-class has no prepare_output_buffer implementation"), (NULL)); - return GST_FLOW_NOT_SUPPORTED; - } -no_buffer: - { - gst_buffer_unref (inbuf); - *outbuf = NULL; - GST_WARNING_OBJECT (trans, "could not get buffer from pool: %s", - gst_flow_get_name (ret)); - return ret; - } -} - -/* FIXME, getrange is broken, need to pull range from the other - * end based on the transform_size result. - */ -static GstFlowReturn -gst_base_transform_getrange (GstPad * pad, GstObject * parent, guint64 offset, - guint length, GstBuffer ** buffer) -{ - GstBaseTransformClass *klass = GST_BASE_TRANSFORM_GET_CLASS (parent); - GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (parent); - GstBaseTransformPrivate *priv = trans->priv; - GstFlowReturn ret; - GstBuffer *inbuf = NULL; - GstBuffer *outbuf = NULL; - - /* Try and generate a buffer, if the sub-class wants more data, - * pull some and repeat until a buffer (or error) is produced */ - do { - ret = klass->generate_output (trans, &outbuf); - - /* Consume the DROPPED return value and go get more data */ - if (ret == GST_BASE_TRANSFORM_FLOW_DROPPED) - ret = GST_FLOW_OK; - - if (ret != GST_FLOW_OK || outbuf != NULL) - break; - - /* No buffer generated, try and pull data */ - ret = gst_pad_pull_range (trans->sinkpad, offset, length, &inbuf); - if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto pull_error; - - if (klass->before_transform) - klass->before_transform (trans, inbuf); - - /* Set discont flag so we can mark the next outgoing buffer */ - if (GST_BUFFER_IS_DISCONT (inbuf)) { - GST_DEBUG_OBJECT (trans, "got DISCONT buffer %p", inbuf); - priv->discont = TRUE; - } - - /* FIXME: Input offsets and lengths need to be translated, as per - * the FIXME above. For now, just advance somewhat */ - offset += gst_buffer_get_size (inbuf); - - ret = klass->submit_input_buffer (trans, priv->discont, inbuf); - if (ret != GST_FLOW_OK) { - if (ret == GST_BASE_TRANSFORM_FLOW_DROPPED) - ret = GST_FLOW_OK; - goto done; - } - } while (ret == GST_FLOW_OK && outbuf == NULL); - - *buffer = outbuf; - if (outbuf) { - /* apply DISCONT flag if the buffer is not yet marked as such */ - if (priv->discont) { - GST_DEBUG_OBJECT (trans, "we have a pending DISCONT"); - if (!GST_BUFFER_IS_DISCONT (outbuf)) { - GST_DEBUG_OBJECT (trans, "marking DISCONT on output buffer"); - outbuf = gst_buffer_make_writable (outbuf); - GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); - } - priv->discont = FALSE; - } - priv->processed++; - } -done: - return ret; - - /* ERRORS */ -pull_error: - { - GST_DEBUG_OBJECT (trans, "failed to pull a buffer: %s", - gst_flow_get_name (ret)); - goto done; - } -} - -/* The flow of the chain function is the reverse of the - * getrange() function - we have data, feed it to the sub-class - * and then iterate, pushing buffers it generates until it either - * wants more data or returns an error */ -static GstFlowReturn -gst_base_transform_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) -{ - GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (parent); - GstBaseTransformClass *klass = GST_BASE_TRANSFORM_GET_CLASS (trans); - GstBaseTransformPrivate *priv = trans->priv; - GstFlowReturn ret; - GstClockTime position = GST_CLOCK_TIME_NONE; - GstClockTime timestamp, duration; - GstBuffer *outbuf = NULL; - - timestamp = GST_BUFFER_TIMESTAMP (buffer); - duration = GST_BUFFER_DURATION (buffer); - - /* calculate end position of the incoming buffer */ - if (timestamp != GST_CLOCK_TIME_NONE) { - if (duration != GST_CLOCK_TIME_NONE) - position = timestamp + duration; - else - position = timestamp; - } - - if (klass->before_transform) - klass->before_transform (trans, buffer); - - /* Set discont flag so we can mark the outgoing buffer */ - if (GST_BUFFER_IS_DISCONT (buffer)) { - GST_DEBUG_OBJECT (trans, "got DISCONT buffer %p", buffer); - priv->discont = TRUE; - } - - /* Takes ownership of input buffer */ - ret = klass->submit_input_buffer (trans, priv->discont, buffer); - if (ret != GST_FLOW_OK) - goto done; - - do { - outbuf = NULL; - - ret = klass->generate_output (trans, &outbuf); - - /* outbuf can be NULL, this means a dropped buffer, if we have a buffer but - * GST_BASE_TRANSFORM_FLOW_DROPPED we will not push either. */ - if (outbuf != NULL) { - if (ret == GST_FLOW_OK) { - GstClockTime position_out = GST_CLOCK_TIME_NONE; - - /* Remember last stop position */ - if (position != GST_CLOCK_TIME_NONE && - trans->segment.format == GST_FORMAT_TIME) - trans->segment.position = position; - - if (GST_BUFFER_TIMESTAMP_IS_VALID (outbuf)) { - position_out = GST_BUFFER_TIMESTAMP (outbuf); - if (GST_BUFFER_DURATION_IS_VALID (outbuf)) - position_out += GST_BUFFER_DURATION (outbuf); - } else if (position != GST_CLOCK_TIME_NONE) { - position_out = position; - } - if (position_out != GST_CLOCK_TIME_NONE - && trans->segment.format == GST_FORMAT_TIME) - priv->position_out = position_out; - - /* apply DISCONT flag if the buffer is not yet marked as such */ - if (trans->priv->discont) { - GST_DEBUG_OBJECT (trans, "we have a pending DISCONT"); - if (!GST_BUFFER_IS_DISCONT (outbuf)) { - GST_DEBUG_OBJECT (trans, "marking DISCONT on output buffer"); - outbuf = gst_buffer_make_writable (outbuf); - GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); - } - priv->discont = FALSE; - } - priv->processed++; - - ret = gst_pad_push (trans->srcpad, outbuf); - } else { - GST_DEBUG_OBJECT (trans, "we got return %s", gst_flow_get_name (ret)); - gst_buffer_unref (outbuf); - } - } - } while (ret == GST_FLOW_OK && outbuf != NULL); - -done: - /* convert internal flow to OK and mark discont for the next buffer. */ - if (ret == GST_BASE_TRANSFORM_FLOW_DROPPED) { - GST_DEBUG_OBJECT (trans, "dropped a buffer, marking DISCONT"); - priv->discont = TRUE; - ret = GST_FLOW_OK; - } - - return ret; -} - -static void -gst_base_transform_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstBaseTransform *trans; - - trans = GST_BASE_TRANSFORM_CAST (object); - - switch (prop_id) { - case PROP_QOS: - gst_base_transform_set_qos_enabled (trans, g_value_get_boolean (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_base_transform_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstBaseTransform *trans; - - trans = GST_BASE_TRANSFORM_CAST (object); - - switch (prop_id) { - case PROP_QOS: - g_value_set_boolean (value, gst_base_transform_is_qos_enabled (trans)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/* not a vmethod of anything, just an internal method */ -static gboolean -gst_base_transform_activate (GstBaseTransform * trans, gboolean active) -{ - GstBaseTransformClass *bclass; - GstBaseTransformPrivate *priv = trans->priv; - gboolean result = TRUE; - - bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); - - if (active) { - GstCaps *incaps, *outcaps; - - if (priv->pad_mode == GST_PAD_MODE_NONE && bclass->start) - result &= bclass->start (trans); - - incaps = gst_pad_get_current_caps (trans->sinkpad); - outcaps = gst_pad_get_current_caps (trans->srcpad); - - GST_OBJECT_LOCK (trans); - if (incaps && outcaps) - priv->have_same_caps = - gst_caps_is_equal (incaps, outcaps) || priv->passthrough; - else - priv->have_same_caps = priv->passthrough; - GST_DEBUG_OBJECT (trans, "have_same_caps %d", priv->have_same_caps); - priv->negotiated = FALSE; - trans->have_segment = FALSE; - gst_segment_init (&trans->segment, GST_FORMAT_UNDEFINED); - priv->position_out = GST_CLOCK_TIME_NONE; - priv->proportion = 1.0; - priv->earliest_time = -1; - priv->discont = FALSE; - priv->processed = 0; - priv->dropped = 0; - GST_OBJECT_UNLOCK (trans); - - if (incaps) - gst_caps_unref (incaps); - if (outcaps) - gst_caps_unref (outcaps); - } else { - /* We must make sure streaming has finished before resetting things - * and calling the ::stop vfunc */ - GST_PAD_STREAM_LOCK (trans->sinkpad); - GST_PAD_STREAM_UNLOCK (trans->sinkpad); - - priv->have_same_caps = FALSE; - /* We can only reset the passthrough mode if the instance told us to - handle it in configure_caps */ - if (bclass->passthrough_on_same_caps) { - gst_base_transform_set_passthrough (trans, FALSE); - } - gst_caps_replace (&priv->cache_caps1, NULL); - gst_caps_replace (&priv->cache_caps2, NULL); - - /* Make sure any left over buffer is freed */ - gst_buffer_replace (&trans->queued_buf, NULL); - - if (priv->pad_mode != GST_PAD_MODE_NONE && bclass->stop) - result &= bclass->stop (trans); - - gst_base_transform_set_allocation (trans, NULL, NULL, NULL, NULL); - } - - return result; -} - -static gboolean -gst_base_transform_sink_activate_mode (GstPad * pad, GstObject * parent, - GstPadMode mode, gboolean active) -{ - gboolean result = FALSE; - GstBaseTransform *trans; - - trans = GST_BASE_TRANSFORM_CAST (parent); - - switch (mode) { - case GST_PAD_MODE_PUSH: - { - result = gst_base_transform_activate (trans, active); - - if (result) - trans->priv->pad_mode = active ? GST_PAD_MODE_PUSH : GST_PAD_MODE_NONE; - - break; - } - default: - result = TRUE; - break; - } - return result; -} - -static gboolean -gst_base_transform_src_activate_mode (GstPad * pad, GstObject * parent, - GstPadMode mode, gboolean active) -{ - gboolean result = FALSE; - GstBaseTransform *trans; - - trans = GST_BASE_TRANSFORM_CAST (parent); - - switch (mode) { - case GST_PAD_MODE_PULL: - { - result = - gst_pad_activate_mode (trans->sinkpad, GST_PAD_MODE_PULL, active); - - if (result) - result &= gst_base_transform_activate (trans, active); - - if (result) - trans->priv->pad_mode = active ? mode : GST_PAD_MODE_NONE; - break; - } - default: - result = TRUE; - break; - } - - return result; -} - -/** - * gst_base_transform_set_passthrough: - * @trans: the #GstBaseTransform to set - * @passthrough: boolean indicating passthrough mode. - * - * Set passthrough mode for this filter by default. This is mostly - * useful for filters that do not care about negotiation. - * - * Always %TRUE for filters which don't implement either a transform - * or transform_ip or generate_output method. - * - * MT safe. - */ -void -gst_base_transform_set_passthrough (GstBaseTransform * trans, - gboolean passthrough) -{ - GstBaseTransformClass *bclass; - - g_return_if_fail (GST_IS_BASE_TRANSFORM (trans)); - - bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); - - GST_OBJECT_LOCK (trans); - if (!passthrough) { - if (bclass->transform_ip || bclass->transform || (bclass->generate_output - && bclass->generate_output != default_generate_output)) - trans->priv->passthrough = FALSE; - } else { - trans->priv->passthrough = TRUE; - } - - GST_DEBUG_OBJECT (trans, "set passthrough %d", trans->priv->passthrough); - GST_OBJECT_UNLOCK (trans); -} - -/** - * gst_base_transform_is_passthrough: - * @trans: the #GstBaseTransform to query - * - * See if @trans is configured as a passthrough transform. - * - * Returns: %TRUE if the transform is configured in passthrough mode. - * - * MT safe. - */ -gboolean -gst_base_transform_is_passthrough (GstBaseTransform * trans) -{ - gboolean result; - - g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), FALSE); - - GST_OBJECT_LOCK (trans); - result = trans->priv->passthrough; - GST_OBJECT_UNLOCK (trans); - - return result; -} - -/** - * gst_base_transform_set_in_place: - * @trans: the #GstBaseTransform to modify - * @in_place: Boolean value indicating that we would like to operate - * on in_place buffers. - * - * Determines whether a non-writable buffer will be copied before passing - * to the transform_ip function. - * - * * Always %TRUE if no transform function is implemented. - * * Always %FALSE if ONLY transform function is implemented. - * - * MT safe. - */ -void -gst_base_transform_set_in_place (GstBaseTransform * trans, gboolean in_place) -{ - GstBaseTransformClass *bclass; - - g_return_if_fail (GST_IS_BASE_TRANSFORM (trans)); - - bclass = GST_BASE_TRANSFORM_GET_CLASS (trans); - - GST_OBJECT_LOCK (trans); - - if (in_place) { - if (bclass->transform_ip) { - GST_DEBUG_OBJECT (trans, "setting in_place TRUE"); - trans->priv->always_in_place = TRUE; - } - } else { - if (bclass->transform) { - GST_DEBUG_OBJECT (trans, "setting in_place FALSE"); - trans->priv->always_in_place = FALSE; - } - } - - GST_OBJECT_UNLOCK (trans); -} - -/** - * gst_base_transform_is_in_place: - * @trans: the #GstBaseTransform to query - * - * See if @trans is configured as a in_place transform. - * - * Returns: %TRUE if the transform is configured in in_place mode. - * - * MT safe. - */ -gboolean -gst_base_transform_is_in_place (GstBaseTransform * trans) -{ - gboolean result; - - g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), FALSE); - - GST_OBJECT_LOCK (trans); - result = trans->priv->always_in_place; - GST_OBJECT_UNLOCK (trans); - - return result; -} - -/** - * gst_base_transform_update_qos: - * @trans: a #GstBaseTransform - * @proportion: the proportion - * @diff: the diff against the clock - * @timestamp: the timestamp of the buffer generating the QoS expressed in - * running_time. - * - * Set the QoS parameters in the transform. This function is called internally - * when a QOS event is received but subclasses can provide custom information - * when needed. - * - * MT safe. - */ -void -gst_base_transform_update_qos (GstBaseTransform * trans, - gdouble proportion, GstClockTimeDiff diff, GstClockTime timestamp) -{ - g_return_if_fail (GST_IS_BASE_TRANSFORM (trans)); - g_return_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp)); - - GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, trans, - "qos: proportion: %lf, diff %" G_GINT64_FORMAT ", timestamp %" - GST_TIME_FORMAT, proportion, diff, GST_TIME_ARGS (timestamp)); - - GST_OBJECT_LOCK (trans); - trans->priv->proportion = proportion; - trans->priv->earliest_time = timestamp + diff; - GST_OBJECT_UNLOCK (trans); -} - -/** - * gst_base_transform_set_qos_enabled: - * @trans: a #GstBaseTransform - * @enabled: new state - * - * Enable or disable QoS handling in the transform. - * - * MT safe. - */ -void -gst_base_transform_set_qos_enabled (GstBaseTransform * trans, gboolean enabled) -{ - g_return_if_fail (GST_IS_BASE_TRANSFORM (trans)); - - GST_CAT_DEBUG_OBJECT (GST_CAT_QOS, trans, "enabled: %d", enabled); - - GST_OBJECT_LOCK (trans); - trans->priv->qos_enabled = enabled; - GST_OBJECT_UNLOCK (trans); -} - -/** - * gst_base_transform_is_qos_enabled: - * @trans: a #GstBaseTransform - * - * Queries if the transform will handle QoS. - * - * Returns: %TRUE if QoS is enabled. - * - * MT safe. - */ -gboolean -gst_base_transform_is_qos_enabled (GstBaseTransform * trans) -{ - gboolean result; - - g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), FALSE); - - GST_OBJECT_LOCK (trans); - result = trans->priv->qos_enabled; - GST_OBJECT_UNLOCK (trans); - - return result; -} - -/** - * gst_base_transform_set_gap_aware: - * @trans: a #GstBaseTransform - * @gap_aware: New state - * - * If @gap_aware is %FALSE (the default), output buffers will have the - * %GST_BUFFER_FLAG_GAP flag unset. - * - * If set to %TRUE, the element must handle output buffers with this flag set - * correctly, i.e. it can assume that the buffer contains neutral data but must - * unset the flag if the output is no neutral data. - * - * MT safe. - */ -void -gst_base_transform_set_gap_aware (GstBaseTransform * trans, gboolean gap_aware) -{ - g_return_if_fail (GST_IS_BASE_TRANSFORM (trans)); - - GST_OBJECT_LOCK (trans); - trans->priv->gap_aware = gap_aware; - GST_DEBUG_OBJECT (trans, "set gap aware %d", trans->priv->gap_aware); - GST_OBJECT_UNLOCK (trans); -} - -/** - * gst_base_transform_set_prefer_passthrough: - * @trans: a #GstBaseTransform - * @prefer_passthrough: New state - * - * If @prefer_passthrough is %TRUE (the default), @trans will check and - * prefer passthrough caps from the list of caps returned by the - * transform_caps vmethod. - * - * If set to %FALSE, the element must order the caps returned from the - * transform_caps function in such a way that the preferred format is - * first in the list. This can be interesting for transforms that can do - * passthrough transforms but prefer to do something else, like a - * capsfilter. - * - * MT safe. - * - * Since: 1.0.1 - */ -void -gst_base_transform_set_prefer_passthrough (GstBaseTransform * trans, - gboolean prefer_passthrough) -{ - g_return_if_fail (GST_IS_BASE_TRANSFORM (trans)); - - GST_OBJECT_LOCK (trans); - trans->priv->prefer_passthrough = prefer_passthrough; - GST_DEBUG_OBJECT (trans, "prefer passthrough %d", prefer_passthrough); - GST_OBJECT_UNLOCK (trans); -} - -/** - * gst_base_transform_reconfigure_sink: - * @trans: a #GstBaseTransform - * - * Instructs @trans to request renegotiation upstream. This function is - * typically called after properties on the transform were set that - * influence the input format. - */ -void -gst_base_transform_reconfigure_sink (GstBaseTransform * trans) -{ - g_return_if_fail (GST_IS_BASE_TRANSFORM (trans)); - - /* push the renegotiate event */ - if (!gst_pad_push_event (GST_BASE_TRANSFORM_SINK_PAD (trans), - gst_event_new_reconfigure ())) - GST_DEBUG_OBJECT (trans, "Renegotiate event wasn't handled"); -} - -/** - * gst_base_transform_reconfigure_src: - * @trans: a #GstBaseTransform - * - * Instructs @trans to renegotiate a new downstream transform on the next - * buffer. This function is typically called after properties on the transform - * were set that influence the output format. - */ -void -gst_base_transform_reconfigure_src (GstBaseTransform * trans) -{ - g_return_if_fail (GST_IS_BASE_TRANSFORM (trans)); - - gst_pad_mark_reconfigure (trans->srcpad); -} - -/** - * gst_base_transform_get_buffer_pool: - * @trans: a #GstBaseTransform - * - * Returns: (nullable) (transfer full): the instance of the #GstBufferPool used - * by @trans; free it after use - */ -GstBufferPool * -gst_base_transform_get_buffer_pool (GstBaseTransform * trans) -{ - g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), NULL); - - if (trans->priv->pool) - return gst_object_ref (trans->priv->pool); - - return NULL; -} - -/** - * gst_base_transform_get_allocator: - * @trans: a #GstBaseTransform - * @allocator: (out) (optional) (nullable) (transfer full): the #GstAllocator - * used - * @params: (out caller-allocates) (optional): the #GstAllocationParams of @allocator - * - * Lets #GstBaseTransform sub-classes know the memory @allocator - * used by the base class and its @params. - * - * Unref the @allocator after use. - */ -void -gst_base_transform_get_allocator (GstBaseTransform * trans, - GstAllocator ** allocator, GstAllocationParams * params) -{ - g_return_if_fail (GST_IS_BASE_TRANSFORM (trans)); - - if (allocator) - *allocator = trans->priv->allocator ? - gst_object_ref (trans->priv->allocator) : NULL; - - if (params) - *params = trans->priv->params; -} - -/** - * gst_base_transform_update_src_caps: - * @trans: a #GstBaseTransform - * @updated_caps: An updated version of the srcpad caps to be pushed - * downstream - * - * Updates the srcpad caps and sends the caps downstream. This function - * can be used by subclasses when they have already negotiated their caps - * but found a change in them (or computed new information). This way, - * they can notify downstream about that change without losing any - * buffer. - * - * Returns: %TRUE if the caps could be sent downstream %FALSE otherwise - * - * Since: 1.6 - */ -gboolean -gst_base_transform_update_src_caps (GstBaseTransform * trans, - GstCaps * updated_caps) -{ - g_return_val_if_fail (GST_IS_BASE_TRANSFORM (trans), FALSE); - - if (gst_pad_push_event (GST_BASE_TRANSFORM_SRC_PAD (trans), - gst_event_new_caps (updated_caps))) { - gst_pad_mark_reconfigure (trans->srcpad); - - return TRUE; - } - - return FALSE; -} diff --git a/libs/gst/base/gstbasetransform.h b/libs/gst/base/gstbasetransform.h deleted file mode 100644 index 0457b89bb2..0000000000 --- a/libs/gst/base/gstbasetransform.h +++ /dev/null @@ -1,372 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> - * 2005 Wim Taymans <wim@fluendo.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_BASE_TRANSFORM_H__ -#define __GST_BASE_TRANSFORM_H__ - -#include <gst/gst.h> -#include <gst/base/base-prelude.h> - -G_BEGIN_DECLS - -#define GST_TYPE_BASE_TRANSFORM (gst_base_transform_get_type()) -#define GST_BASE_TRANSFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASE_TRANSFORM,GstBaseTransform)) -#define GST_BASE_TRANSFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASE_TRANSFORM,GstBaseTransformClass)) -#define GST_BASE_TRANSFORM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_BASE_TRANSFORM,GstBaseTransformClass)) -#define GST_IS_BASE_TRANSFORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASE_TRANSFORM)) -#define GST_IS_BASE_TRANSFORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASE_TRANSFORM)) -#define GST_BASE_TRANSFORM_CAST(obj) ((GstBaseTransform *)(obj)) - -/** - * GST_BASE_TRANSFORM_SINK_NAME: - * - * The name of the templates for the sink pad. - */ -#define GST_BASE_TRANSFORM_SINK_NAME "sink" -/** - * GST_BASE_TRANSFORM_SRC_NAME: - * - * The name of the templates for the source pad. - */ -#define GST_BASE_TRANSFORM_SRC_NAME "src" - -/** - * GST_BASE_TRANSFORM_SRC_PAD: - * @obj: base transform instance - * - * Gives the pointer to the source #GstPad object of the element. - */ -#define GST_BASE_TRANSFORM_SRC_PAD(obj) (GST_BASE_TRANSFORM_CAST (obj)->srcpad) - -/** - * GST_BASE_TRANSFORM_SINK_PAD: - * @obj: base transform instance - * - * Gives the pointer to the sink #GstPad object of the element. - */ -#define GST_BASE_TRANSFORM_SINK_PAD(obj) (GST_BASE_TRANSFORM_CAST (obj)->sinkpad) - -/** - * GST_BASE_TRANSFORM_FLOW_DROPPED: - * - * A #GstFlowReturn that can be returned from transform and transform_ip to - * indicate that no output buffer was generated. - */ -#define GST_BASE_TRANSFORM_FLOW_DROPPED GST_FLOW_CUSTOM_SUCCESS - -typedef struct _GstBaseTransform GstBaseTransform; -typedef struct _GstBaseTransformClass GstBaseTransformClass; -typedef struct _GstBaseTransformPrivate GstBaseTransformPrivate; - -/** - * GstBaseTransform: - * - * The opaque #GstBaseTransform data structure. - */ -struct _GstBaseTransform { - GstElement element; - - /*< protected >*/ - /* source and sink pads */ - GstPad *sinkpad; - GstPad *srcpad; - - /* MT-protected (with STREAM_LOCK) */ - gboolean have_segment; - GstSegment segment; - /* Default submit_input_buffer places the buffer here, - * for consumption by the generate_output method: */ - GstBuffer *queued_buf; - - /*< private >*/ - GstBaseTransformPrivate *priv; - - gpointer _gst_reserved[GST_PADDING_LARGE-1]; -}; - -/** - * GstBaseTransformClass: - * @parent_class: Element parent class - * @passthrough_on_same_caps: If set to %TRUE, passthrough mode will be - * automatically enabled if the caps are the same. - * Set to %FALSE by default. - * @transform_ip_on_passthrough: If set to %TRUE, @transform_ip will be called in - * passthrough mode. The passed buffer might not be - * writable. When %FALSE, neither @transform nor - * @transform_ip will be called in passthrough mode. - * Set to %TRUE by default. - * @transform_caps: Optional. Given the pad in this direction and the given - * caps, what caps are allowed on the other pad in this - * element ? - * @fixate_caps: Optional. Given the pad in this direction and the given - * caps, fixate the caps on the other pad. The function takes - * ownership of @othercaps and returns a fixated version of - * @othercaps. @othercaps is not guaranteed to be writable. - * @accept_caps: Optional. - * Subclasses can override this method to check if @caps can be - * handled by the element. The default implementation might not be - * the most optimal way to check this in all cases. - * @set_caps: Allows the subclass to be notified of the actual caps set. - * @query: Optional. - * Handle a requested query. Subclasses that implement this - * must chain up to the parent if they didn't handle the - * query - * @decide_allocation: Setup the allocation parameters for allocating output - * buffers. The passed in query contains the result of the - * downstream allocation query. This function is only called - * when not operating in passthrough mode. The default - * implementation will remove all memory dependent metadata. - * If there is a @filter_meta method implementation, it will - * be called for all metadata API in the downstream query, - * otherwise the metadata API is removed. - * @filter_meta: Return %TRUE if the metadata API should be proposed in the - * upstream allocation query. The default implementation is %NULL - * and will cause all metadata to be removed. - * @propose_allocation: Propose buffer allocation parameters for upstream elements. - * This function must be implemented if the element reads or - * writes the buffer content. The query that was passed to - * the decide_allocation is passed in this method (or %NULL - * when the element is in passthrough mode). The default - * implementation will pass the query downstream when in - * passthrough mode and will copy all the filtered metadata - * API in non-passthrough mode. - * @transform_size: Optional. Given the size of a buffer in the given direction - * with the given caps, calculate the size in bytes of a buffer - * on the other pad with the given other caps. - * The default implementation uses get_unit_size and keeps - * the number of units the same. - * @get_unit_size: Required if the transform is not in-place. - * Get the size in bytes of one unit for the given caps. - * @start: Optional. - * Called when the element starts processing. - * Allows opening external resources. - * @stop: Optional. - * Called when the element stops processing. - * Allows closing external resources. - * @sink_event: Optional. - * Event handler on the sink pad. The default implementation - * handles the event and forwards it downstream. - * @src_event: Optional. - * Event handler on the source pad. The default implementation - * handles the event and forwards it upstream. - * @prepare_output_buffer: Optional. - * Subclasses can override this to do their own - * allocation of output buffers. Elements that only do - * analysis can return a subbuffer or even just - * return a reference to the input buffer (if in - * passthrough mode). The default implementation will - * use the negotiated allocator or bufferpool and - * transform_size to allocate an output buffer or it - * will return the input buffer in passthrough mode. - * @copy_metadata: Optional. - * Copy the metadata from the input buffer to the output buffer. - * The default implementation will copy the flags, timestamps and - * offsets of the buffer. - * @transform_meta: Optional. Transform the metadata on the input buffer to the - * output buffer. By default this method copies all meta without - * tags. Subclasses can implement this method and return %TRUE if - * the metadata is to be copied. - * @before_transform: Optional. - * This method is called right before the base class will - * start processing. Dynamic properties or other delayed - * configuration could be performed in this method. - * @transform: Required if the element does not operate in-place. - * Transforms one incoming buffer to one outgoing buffer. - * The function is allowed to change size/timestamp/duration - * of the outgoing buffer. - * @transform_ip: Required if the element operates in-place. - * Transform the incoming buffer in-place. - * @submit_input_buffer: Function which accepts a new input buffer and pre-processes it. - * The default implementation performs caps (re)negotiation, then - * QoS if needed, and places the input buffer into the @queued_buf - * member variable. If the buffer is dropped due to QoS, it returns - * GST_BASE_TRANSFORM_FLOW_DROPPED. If this input buffer is not - * contiguous with any previous input buffer, then @is_discont - * is set to %TRUE. (Since: 1.6) - * @generate_output: Called after each new input buffer is submitted repeatedly - * until it either generates an error or fails to generate an output - * buffer. The default implementation takes the contents of the - * @queued_buf variable, generates an output buffer if needed - * by calling the class @prepare_output_buffer, and then - * calls either @transform or @transform_ip. Elements that don't - * do 1-to-1 transformations of input to output buffers can either - * return GST_BASE_TRANSFORM_FLOW_DROPPED or simply not generate - * an output buffer until they are ready to do so. (Since: 1.6) - * - * Subclasses can override any of the available virtual methods or not, as - * needed. At minimum either @transform or @transform_ip need to be overridden. - * If the element can overwrite the input data with the results (data is of the - * same type and quantity) it should provide @transform_ip. - */ -struct _GstBaseTransformClass { - GstElementClass parent_class; - - /*< public >*/ - gboolean passthrough_on_same_caps; - gboolean transform_ip_on_passthrough; - - /* virtual methods for subclasses */ - GstCaps* (*transform_caps) (GstBaseTransform *trans, - GstPadDirection direction, - GstCaps *caps, GstCaps *filter); - /** - * GstBaseTransformClass::fixate_caps: - * @othercaps: (transfer full): - */ - GstCaps* (*fixate_caps) (GstBaseTransform *trans, - GstPadDirection direction, GstCaps *caps, - GstCaps *othercaps); - gboolean (*accept_caps) (GstBaseTransform *trans, GstPadDirection direction, - GstCaps *caps); - gboolean (*set_caps) (GstBaseTransform *trans, GstCaps *incaps, - GstCaps *outcaps); - gboolean (*query) (GstBaseTransform *trans, GstPadDirection direction, - GstQuery *query); - - /* decide allocation query for output buffers */ - gboolean (*decide_allocation) (GstBaseTransform *trans, GstQuery *query); - gboolean (*filter_meta) (GstBaseTransform *trans, GstQuery *query, - GType api, const GstStructure *params); - - /* propose allocation query parameters for input buffers */ - gboolean (*propose_allocation) (GstBaseTransform *trans, GstQuery *decide_query, - GstQuery *query); - - /** - * GstBaseTransformClass::transform_size: - * @othersize: (out): - */ - gboolean (*transform_size) (GstBaseTransform *trans, - GstPadDirection direction, - GstCaps *caps, gsize size, - GstCaps *othercaps, gsize *othersize); - - /** - * GstBaseTransformClass::get_unit_size: - * @size: (out): - */ - gboolean (*get_unit_size) (GstBaseTransform *trans, GstCaps *caps, - gsize *size); - - /* states */ - gboolean (*start) (GstBaseTransform *trans); - gboolean (*stop) (GstBaseTransform *trans); - - /* sink and src pad event handlers */ - /** - * GstBaseTransformClass::sink_event: - * @event: (transfer full): - */ - gboolean (*sink_event) (GstBaseTransform *trans, GstEvent *event); - /** - * GstBaseTransformClass::src_event: - * @event: (transfer full): - */ - gboolean (*src_event) (GstBaseTransform *trans, GstEvent *event); - - /** - * GstBaseTransformClass::prepare_output_buffer: - * @outbuf: (out): - */ - GstFlowReturn (*prepare_output_buffer) (GstBaseTransform * trans, - GstBuffer *input, GstBuffer **outbuf); - - /* metadata */ - gboolean (*copy_metadata) (GstBaseTransform *trans, GstBuffer *input, - GstBuffer *outbuf); - gboolean (*transform_meta) (GstBaseTransform *trans, GstBuffer *outbuf, - GstMeta *meta, GstBuffer *inbuf); - - void (*before_transform) (GstBaseTransform *trans, GstBuffer *buffer); - - /* transform */ - GstFlowReturn (*transform) (GstBaseTransform *trans, GstBuffer *inbuf, - GstBuffer *outbuf); - GstFlowReturn (*transform_ip) (GstBaseTransform *trans, GstBuffer *buf); - - GstFlowReturn (*submit_input_buffer) (GstBaseTransform *trans, gboolean is_discont, GstBuffer *input); - - /** - * GstBaseTransformClass::generate_output: - * @outbuf: (out): - */ - GstFlowReturn (*generate_output) (GstBaseTransform *trans, GstBuffer **outbuf); - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING_LARGE - 2]; -}; - -GST_BASE_API -GType gst_base_transform_get_type (void); - -GST_BASE_API -void gst_base_transform_set_passthrough (GstBaseTransform *trans, - gboolean passthrough); -GST_BASE_API -gboolean gst_base_transform_is_passthrough (GstBaseTransform *trans); - -GST_BASE_API -void gst_base_transform_set_in_place (GstBaseTransform *trans, - gboolean in_place); -GST_BASE_API -gboolean gst_base_transform_is_in_place (GstBaseTransform *trans); - -GST_BASE_API -void gst_base_transform_update_qos (GstBaseTransform *trans, - gdouble proportion, - GstClockTimeDiff diff, - GstClockTime timestamp); -GST_BASE_API -void gst_base_transform_set_qos_enabled (GstBaseTransform *trans, - gboolean enabled); -GST_BASE_API -gboolean gst_base_transform_is_qos_enabled (GstBaseTransform *trans); - -GST_BASE_API -void gst_base_transform_set_gap_aware (GstBaseTransform *trans, - gboolean gap_aware); -GST_BASE_API -void gst_base_transform_set_prefer_passthrough (GstBaseTransform *trans, - gboolean prefer_passthrough); -GST_BASE_API -GstBufferPool * gst_base_transform_get_buffer_pool (GstBaseTransform *trans); - -GST_BASE_API -void gst_base_transform_get_allocator (GstBaseTransform *trans, - GstAllocator **allocator, - GstAllocationParams *params); -GST_BASE_API -void gst_base_transform_reconfigure_sink (GstBaseTransform *trans); - -GST_BASE_API -void gst_base_transform_reconfigure_src (GstBaseTransform *trans); - -GST_BASE_API -gboolean gst_base_transform_update_src_caps (GstBaseTransform *trans, - GstCaps *updated_caps); - -GST_BASE_API -gboolean gst_base_transform_reconfigure (GstBaseTransform * trans); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstBaseTransform, gst_object_unref) - -G_END_DECLS - -#endif /* __GST_BASE_TRANSFORM_H__ */ diff --git a/libs/gst/base/gstbitreader-docs.h b/libs/gst/base/gstbitreader-docs.h deleted file mode 100644 index d2842d6382..0000000000 --- a/libs/gst/base/gstbitreader-docs.h +++ /dev/null @@ -1,141 +0,0 @@ -/* GStreamer bit reader dummy header for gtk-doc - * Copyright (C) 2010 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., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/* This header is not installed, it just contains stuff for gtk-doc to parse, - * in particular docs and some dummy function declarations for the static - * inline functions we generate via macros in gstbitreader.h. - */ - -#error "This header should never be included in code, it is only for gtk-doc" - -/** - * gst_bit_reader_skip_unchecked: - * @reader: a #GstBitReader instance - * @nbits: the number of bits to skip - * - * Skips @nbits bits of the #GstBitReader instance without checking if there - * are enough bits available in the bit reader. - */ -void gst_bit_reader_skip_unchecked (GstBitReader * reader, guint nbits); - -/** - * gst_bit_reader_skip_to_byte_unchecked: - * @reader: a #GstBitReader instance - * - * Skips until the next byte without checking if there are enough bits - * available in the bit reader. - */ -void gst_bit_reader_skip_to_byte_unchecked (GstBitReader * reader); - -/** - * gst_bit_reader_get_bits_uint8_unchecked: - * @reader: a #GstBitReader instance - * @nbits: number of bits to read - * - * Read @nbits bits into @val and update the current position without - * checking if there are enough bits available in the bit reader. - * - * Returns: unsigned 8 bit integer with the bits. - */ -guint8 gst_bit_reader_get_bits_uint8_unchecked (GstBitReader *reader, guint nbits); - -/** - * gst_bit_reader_peek_bits_uint8_unchecked: - * @reader: a #GstBitReader instance - * @nbits: number of bits to read - * - * Read @nbits bits into @val but keep the current position without - * checking if there are enough bits available in the bit reader - * - * Returns: unsigned 8 bit integer with the bits. - */ -guint8 gst_bit_reader_peek_bits_uint8_unchecked (const GstBitReader *reader, guint nbits); - -/** - * gst_bit_reader_get_bits_uint16_unchecked: - * @reader: a #GstBitReader instance - * @nbits: number of bits to read - * - * Read @nbits bits into @val and update the current position without - * checking if there are enough bits available in the bit reader. - * - * Returns: unsigned 16 bit integer with the bits. - */ -guint16 gst_bit_reader_get_bits_uint16_unchecked (GstBitReader *reader, guint nbits); - -/** - * gst_bit_reader_peek_bits_uint16_unchecked: - * @reader: a #GstBitReader instance - * @nbits: number of bits to read - * - * Read @nbits bits into @val but keep the current position without - * checking if there are enough bits available in the bit reader - * - * Returns: unsigned 16 bit integer with the bits. - */ -guint16 gst_bit_reader_peek_bits_uint16_unchecked (const GstBitReader *reader, guint nbits); - -/** - * gst_bit_reader_get_bits_uint32_unchecked: - * @reader: a #GstBitReader instance - * @nbits: number of bits to read - * - * Read @nbits bits into @val and update the current position without - * checking if there are enough bits available in the bit reader. - * - * Returns: unsigned 32 bit integer with the bits. - */ -guint32 gst_bit_reader_get_bits_uint32_unchecked (GstBitReader *reader, guint nbits); - -/** - * gst_bit_reader_peek_bits_uint32_unchecked: - * @reader: a #GstBitReader instance - * @nbits: number of bits to read - * - * Read @nbits bits into @val but keep the current position without - * checking if there are enough bits available in the bit reader - * - * Returns: unsigned 32 bit integer with the bits. - */ -guint32 gst_bit_reader_peek_bits_uint32_unchecked (const GstBitReader *reader, guint nbits); - -/** - * gst_bit_reader_get_bits_uint64_unchecked: - * @reader: a #GstBitReader instance - * @nbits: number of bits to read - * - * Read @nbits bits into @val and update the current position without - * checking if there are enough bits available in the bit reader. - * - * Returns: unsigned 64 bit integer with the bits. - */ -guint64 gst_bit_reader_get_bits_uint64_unchecked (GstBitReader *reader, guint nbits); - -/** - * gst_bit_reader_peek_bits_uint64_unchecked: - * @reader: a #GstBitReader instance - * @nbits: number of bits to read - * - * Read @nbits bits into @val but keep the current position without - * checking if there are enough bits available in the bit reader - * - * Returns: unsigned 64 bit integer with the bits. - */ -guint64 gst_bit_reader_peek_bits_uint64_unchecked (const GstBitReader *reader, guint nbits); - diff --git a/libs/gst/base/gstbitreader.c b/libs/gst/base/gstbitreader.c deleted file mode 100644 index db97ad51a2..0000000000 --- a/libs/gst/base/gstbitreader.c +++ /dev/null @@ -1,307 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2008 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., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#define GST_BIT_READER_DISABLE_INLINES -#include "gstbitreader.h" - -#include <string.h> - -/** - * SECTION:gstbitreader - * @title: GstBitReader - * @short_description: Reads any number of bits from a memory buffer - * @symbols: - * - gst_bit_reader_skip_unchecked - * - gst_bit_reader_skip_to_byte_unchecked - * - gst_bit_reader_get_bits_uint8_unchecked - * - gst_bit_reader_peek_bits_uint8_unchecked - * - gst_bit_reader_get_bits_uint16_unchecked - * - gst_bit_reader_peek_bits_uint16_unchecked - * - gst_bit_reader_get_bits_uint32_unchecked - * - gst_bit_reader_peek_bits_uint32_unchecked - * - gst_bit_reader_get_bits_uint64_unchecked - * - gst_bit_reader_peek_bits_uint64_unchecked - * - * #GstBitReader provides a bit reader that can read any number of bits - * from a memory buffer. It provides functions for reading any number of bits - * into 8, 16, 32 and 64 bit variables. - */ - -/** - * gst_bit_reader_new: (skip) - * @data: (array length=size): Data from which the #GstBitReader - * should read - * @size: Size of @data in bytes - * - * Create a new #GstBitReader instance, which will read from @data. - * - * Free-function: gst_bit_reader_free - * - * Returns: (transfer full): a new #GstBitReader instance - */ -GstBitReader * -gst_bit_reader_new (const guint8 * data, guint size) -{ - GstBitReader *ret = g_slice_new0 (GstBitReader); - - ret->data = data; - ret->size = size; - - return ret; -} - -/** - * gst_bit_reader_free: - * @reader: (in) (transfer full): a #GstBitReader instance - * - * Frees a #GstBitReader instance, which was previously allocated by - * gst_bit_reader_new(). - */ -void -gst_bit_reader_free (GstBitReader * reader) -{ - g_return_if_fail (reader != NULL); - - g_slice_free (GstBitReader, reader); -} - -/** - * gst_bit_reader_init: - * @reader: a #GstBitReader instance - * @data: (in) (array length=size): data from which the bit reader should read - * @size: Size of @data in bytes - * - * Initializes a #GstBitReader instance to read from @data. This function - * can be called on already initialized instances. - */ -void -gst_bit_reader_init (GstBitReader * reader, const guint8 * data, guint size) -{ - g_return_if_fail (reader != NULL); - - reader->data = data; - reader->size = size; - reader->byte = reader->bit = 0; -} - -/** - * gst_bit_reader_set_pos: - * @reader: a #GstBitReader instance - * @pos: The new position in bits - * - * Sets the new position of a #GstBitReader instance to @pos in bits. - * - * Returns: %TRUE if the position could be set successfully, %FALSE - * otherwise. - */ -gboolean -gst_bit_reader_set_pos (GstBitReader * reader, guint pos) -{ - g_return_val_if_fail (reader != NULL, FALSE); - - if (pos > reader->size * 8) - return FALSE; - - reader->byte = pos / 8; - reader->bit = pos % 8; - - return TRUE; -} - -/** - * gst_bit_reader_get_pos: - * @reader: a #GstBitReader instance - * - * Returns the current position of a #GstBitReader instance in bits. - * - * Returns: The current position of @reader in bits. - */ -guint -gst_bit_reader_get_pos (const GstBitReader * reader) -{ - return _gst_bit_reader_get_pos_inline (reader); -} - -/** - * gst_bit_reader_get_remaining: - * @reader: a #GstBitReader instance - * - * Returns the remaining number of bits of a #GstBitReader instance. - * - * Returns: The remaining number of bits of @reader instance. - */ -guint -gst_bit_reader_get_remaining (const GstBitReader * reader) -{ - return _gst_bit_reader_get_remaining_inline (reader); -} - -/** - * gst_bit_reader_get_size: - * @reader: a #GstBitReader instance - * - * Returns the total number of bits of a #GstBitReader instance. - * - * Returns: The total number of bits of @reader instance. - */ -guint -gst_bit_reader_get_size (const GstBitReader * reader) -{ - return _gst_bit_reader_get_size_inline (reader); -} - -/** - * gst_bit_reader_skip: - * @reader: a #GstBitReader instance - * @nbits: the number of bits to skip - * - * Skips @nbits bits of the #GstBitReader instance. - * - * Returns: %TRUE if @nbits bits could be skipped, %FALSE otherwise. - */ -gboolean -gst_bit_reader_skip (GstBitReader * reader, guint nbits) -{ - return _gst_bit_reader_skip_inline (reader, nbits); -} - -/** - * gst_bit_reader_skip_to_byte: - * @reader: a #GstBitReader instance - * - * Skips until the next byte. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ -gboolean -gst_bit_reader_skip_to_byte (GstBitReader * reader) -{ - return _gst_bit_reader_skip_to_byte_inline (reader); -} - -/** - * gst_bit_reader_get_bits_uint8: - * @reader: a #GstBitReader instance - * @val: (out): Pointer to a #guint8 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_bit_reader_get_bits_uint16: - * @reader: a #GstBitReader instance - * @val: (out): Pointer to a #guint16 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_bit_reader_get_bits_uint32: - * @reader: a #GstBitReader instance - * @val: (out): Pointer to a #guint32 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_bit_reader_get_bits_uint64: - * @reader: a #GstBitReader instance - * @val: (out): Pointer to a #guint64 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_bit_reader_peek_bits_uint8: - * @reader: a #GstBitReader instance - * @val: (out): Pointer to a #guint8 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_bit_reader_peek_bits_uint16: - * @reader: a #GstBitReader instance - * @val: (out): Pointer to a #guint16 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_bit_reader_peek_bits_uint32: - * @reader: a #GstBitReader instance - * @val: (out): Pointer to a #guint32 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_bit_reader_peek_bits_uint64: - * @reader: a #GstBitReader instance - * @val: (out): Pointer to a #guint64 to store the result - * @nbits: number of bits to read - * - * Read @nbits bits into @val but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -#define GST_BIT_READER_READ_BITS(bits) \ -gboolean \ -gst_bit_reader_peek_bits_uint##bits (const GstBitReader *reader, guint##bits *val, guint nbits) \ -{ \ - return _gst_bit_reader_peek_bits_uint##bits##_inline (reader, val, nbits); \ -} \ -\ -gboolean \ -gst_bit_reader_get_bits_uint##bits (GstBitReader *reader, guint##bits *val, guint nbits) \ -{ \ - return _gst_bit_reader_get_bits_uint##bits##_inline (reader, val, nbits); \ -} - -GST_BIT_READER_READ_BITS (8); -GST_BIT_READER_READ_BITS (16); -GST_BIT_READER_READ_BITS (32); -GST_BIT_READER_READ_BITS (64); diff --git a/libs/gst/base/gstbitreader.h b/libs/gst/base/gstbitreader.h deleted file mode 100644 index 380edd3fe6..0000000000 --- a/libs/gst/base/gstbitreader.h +++ /dev/null @@ -1,328 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2008 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., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_BIT_READER_H__ -#define __GST_BIT_READER_H__ - -#include <gst/gst.h> -#include <gst/base/base-prelude.h> - -/* FIXME: inline functions */ - -G_BEGIN_DECLS - -#define GST_BIT_READER(reader) ((GstBitReader *) (reader)) - -/** - * GstBitReader: - * @data: (array length=size): Data from which the bit reader will - * read - * @size: Size of @data in bytes - * @byte: Current byte position - * @bit: Bit position in the current byte - * - * A bit reader instance. - */ -typedef struct { - const guint8 *data; - guint size; - - guint byte; /* Byte position */ - guint bit; /* Bit position in the current byte */ - - /* < private > */ - gpointer _gst_reserved[GST_PADDING]; -} GstBitReader; - -GST_BASE_API -GstBitReader * gst_bit_reader_new (const guint8 *data, guint size) G_GNUC_MALLOC; - -GST_BASE_API -void gst_bit_reader_free (GstBitReader *reader); - -GST_BASE_API -void gst_bit_reader_init (GstBitReader *reader, const guint8 *data, guint size); - -GST_BASE_API -gboolean gst_bit_reader_set_pos (GstBitReader *reader, guint pos); - -GST_BASE_API -guint gst_bit_reader_get_pos (const GstBitReader *reader); - -GST_BASE_API -guint gst_bit_reader_get_remaining (const GstBitReader *reader); - -GST_BASE_API -guint gst_bit_reader_get_size (const GstBitReader *reader); - -GST_BASE_API -gboolean gst_bit_reader_skip (GstBitReader *reader, guint nbits); - -GST_BASE_API -gboolean gst_bit_reader_skip_to_byte (GstBitReader *reader); - -GST_BASE_API -gboolean gst_bit_reader_get_bits_uint8 (GstBitReader *reader, guint8 *val, guint nbits); - -GST_BASE_API -gboolean gst_bit_reader_get_bits_uint16 (GstBitReader *reader, guint16 *val, guint nbits); - -GST_BASE_API -gboolean gst_bit_reader_get_bits_uint32 (GstBitReader *reader, guint32 *val, guint nbits); - -GST_BASE_API -gboolean gst_bit_reader_get_bits_uint64 (GstBitReader *reader, guint64 *val, guint nbits); - -GST_BASE_API -gboolean gst_bit_reader_peek_bits_uint8 (const GstBitReader *reader, guint8 *val, guint nbits); - -GST_BASE_API -gboolean gst_bit_reader_peek_bits_uint16 (const GstBitReader *reader, guint16 *val, guint nbits); - -GST_BASE_API -gboolean gst_bit_reader_peek_bits_uint32 (const GstBitReader *reader, guint32 *val, guint nbits); - -GST_BASE_API -gboolean gst_bit_reader_peek_bits_uint64 (const GstBitReader *reader, guint64 *val, guint nbits); - -/** - * GST_BIT_READER_INIT: - * @data: Data from which the #GstBitReader should read - * @size: Size of @data in bytes - * - * A #GstBitReader must be initialized with this macro, before it can be - * used. This macro can used be to initialize a variable, but it cannot - * be assigned to a variable. In that case you have to use - * gst_bit_reader_init(). - */ -#define GST_BIT_READER_INIT(data, size) {data, size, 0, 0} - -/* Unchecked variants */ - -static inline void -gst_bit_reader_skip_unchecked (GstBitReader * reader, guint nbits) -{ - reader->bit += nbits; - reader->byte += reader->bit / 8; - reader->bit = reader->bit % 8; -} - -static inline void -gst_bit_reader_skip_to_byte_unchecked (GstBitReader * reader) -{ - if (reader->bit) { - reader->bit = 0; - reader->byte++; - } -} - -#define __GST_BIT_READER_READ_BITS_UNCHECKED(bits) \ -static inline guint##bits \ -gst_bit_reader_peek_bits_uint##bits##_unchecked (const GstBitReader *reader, guint nbits) \ -{ \ - guint##bits ret = 0; \ - const guint8 *data; \ - guint byte, bit; \ - \ - data = reader->data; \ - byte = reader->byte; \ - bit = reader->bit; \ - \ - while (nbits > 0) { \ - guint toread = MIN (nbits, 8 - bit); \ - \ - ret <<= toread; \ - ret |= (data[byte] & (0xff >> bit)) >> (8 - toread - bit); \ - \ - bit += toread; \ - if (bit >= 8) { \ - byte++; \ - bit = 0; \ - } \ - nbits -= toread; \ - } \ - \ - return ret; \ -} \ -\ -static inline guint##bits \ -gst_bit_reader_get_bits_uint##bits##_unchecked (GstBitReader *reader, guint nbits) \ -{ \ - guint##bits ret; \ - \ - ret = gst_bit_reader_peek_bits_uint##bits##_unchecked (reader, nbits); \ - \ - gst_bit_reader_skip_unchecked (reader, nbits); \ - \ - return ret; \ -} - -__GST_BIT_READER_READ_BITS_UNCHECKED (8) -__GST_BIT_READER_READ_BITS_UNCHECKED (16) -__GST_BIT_READER_READ_BITS_UNCHECKED (32) -__GST_BIT_READER_READ_BITS_UNCHECKED (64) - -#undef __GST_BIT_READER_READ_BITS_UNCHECKED - -/* unchecked variants -- do not use */ - -static inline guint -_gst_bit_reader_get_size_unchecked (const GstBitReader * reader) -{ - return reader->size * 8; -} - -static inline guint -_gst_bit_reader_get_pos_unchecked (const GstBitReader * reader) -{ - return reader->byte * 8 + reader->bit; -} - -static inline guint -_gst_bit_reader_get_remaining_unchecked (const GstBitReader * reader) -{ - return reader->size * 8 - (reader->byte * 8 + reader->bit); -} - -/* inlined variants -- do not use directly */ -static inline guint -_gst_bit_reader_get_size_inline (const GstBitReader * reader) -{ - g_return_val_if_fail (reader != NULL, 0); - - return _gst_bit_reader_get_size_unchecked (reader); -} - -static inline guint -_gst_bit_reader_get_pos_inline (const GstBitReader * reader) -{ - g_return_val_if_fail (reader != NULL, 0); - - return _gst_bit_reader_get_pos_unchecked (reader); -} - -static inline guint -_gst_bit_reader_get_remaining_inline (const GstBitReader * reader) -{ - g_return_val_if_fail (reader != NULL, 0); - - return _gst_bit_reader_get_remaining_unchecked (reader); -} - -static inline gboolean -_gst_bit_reader_skip_inline (GstBitReader * reader, guint nbits) -{ - g_return_val_if_fail (reader != NULL, FALSE); - - if (_gst_bit_reader_get_remaining_unchecked (reader) < nbits) - return FALSE; - - gst_bit_reader_skip_unchecked (reader, nbits); - - return TRUE; -} - -static inline gboolean -_gst_bit_reader_skip_to_byte_inline (GstBitReader * reader) -{ - g_return_val_if_fail (reader != NULL, FALSE); - - if (reader->byte > reader->size) - return FALSE; - - gst_bit_reader_skip_to_byte_unchecked (reader); - - return TRUE; -} - -#define __GST_BIT_READER_READ_BITS_INLINE(bits) \ -static inline gboolean \ -_gst_bit_reader_get_bits_uint##bits##_inline (GstBitReader *reader, guint##bits *val, guint nbits) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - g_return_val_if_fail (nbits <= bits, FALSE); \ - \ - if (_gst_bit_reader_get_remaining_unchecked (reader) < nbits) \ - return FALSE; \ -\ - *val = gst_bit_reader_get_bits_uint##bits##_unchecked (reader, nbits); \ - return TRUE; \ -} \ -\ -static inline gboolean \ -_gst_bit_reader_peek_bits_uint##bits##_inline (const GstBitReader *reader, guint##bits *val, guint nbits) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - g_return_val_if_fail (nbits <= bits, FALSE); \ - \ - if (_gst_bit_reader_get_remaining_unchecked (reader) < nbits) \ - return FALSE; \ -\ - *val = gst_bit_reader_peek_bits_uint##bits##_unchecked (reader, nbits); \ - return TRUE; \ -} - -__GST_BIT_READER_READ_BITS_INLINE (8) -__GST_BIT_READER_READ_BITS_INLINE (16) -__GST_BIT_READER_READ_BITS_INLINE (32) -__GST_BIT_READER_READ_BITS_INLINE (64) - -#undef __GST_BIT_READER_READ_BITS_INLINE - -#ifndef GST_BIT_READER_DISABLE_INLINES - -#define gst_bit_reader_get_size(reader) \ - _gst_bit_reader_get_size_inline (reader) -#define gst_bit_reader_get_pos(reader) \ - _gst_bit_reader_get_pos_inline (reader) -#define gst_bit_reader_get_remaining(reader) \ - _gst_bit_reader_get_remaining_inline (reader) - -/* we use defines here so we can add the G_LIKELY() */ - -#define gst_bit_reader_skip(reader, nbits)\ - G_LIKELY (_gst_bit_reader_skip_inline(reader, nbits)) -#define gst_bit_reader_skip_to_byte(reader)\ - G_LIKELY (_gst_bit_reader_skip_to_byte_inline(reader)) - -#define gst_bit_reader_get_bits_uint8(reader, val, nbits) \ - G_LIKELY (_gst_bit_reader_get_bits_uint8_inline (reader, val, nbits)) -#define gst_bit_reader_get_bits_uint16(reader, val, nbits) \ - G_LIKELY (_gst_bit_reader_get_bits_uint16_inline (reader, val, nbits)) -#define gst_bit_reader_get_bits_uint32(reader, val, nbits) \ - G_LIKELY (_gst_bit_reader_get_bits_uint32_inline (reader, val, nbits)) -#define gst_bit_reader_get_bits_uint64(reader, val, nbits) \ - G_LIKELY (_gst_bit_reader_get_bits_uint64_inline (reader, val, nbits)) - -#define gst_bit_reader_peek_bits_uint8(reader, val, nbits) \ - G_LIKELY (_gst_bit_reader_peek_bits_uint8_inline (reader, val, nbits)) -#define gst_bit_reader_peek_bits_uint16(reader, val, nbits) \ - G_LIKELY (_gst_bit_reader_peek_bits_uint16_inline (reader, val, nbits)) -#define gst_bit_reader_peek_bits_uint32(reader, val, nbits) \ - G_LIKELY (_gst_bit_reader_peek_bits_uint32_inline (reader, val, nbits)) -#define gst_bit_reader_peek_bits_uint64(reader, val, nbits) \ - G_LIKELY (_gst_bit_reader_peek_bits_uint64_inline (reader, val, nbits)) -#endif - -G_END_DECLS - -#endif /* __GST_BIT_READER_H__ */ diff --git a/libs/gst/base/gstbitwriter-docs.h b/libs/gst/base/gstbitwriter-docs.h deleted file mode 100644 index bc44db86a9..0000000000 --- a/libs/gst/base/gstbitwriter-docs.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * GStreamer bit writer dummy header for gtk-doc - * - * Copyright (C) 2013 Intel Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 - * 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -/* This header is not installed, it just contains stuff for gtk-doc to parse, - * in particular docs and some dummy function declarations for the static - * inline functions we generate via macros in gstbitwriter.h. - */ - -#error "This header should never be included in code, it is only for gtk-doc" - -/** - * gst_bit_writer_put_bits_uint8_unchecked: - * @bitwriter: a #GstBitWriter instance - * @value: value of #guint8 to write - * @nbits: number of bits to write - * - * Write @nbits bits of @value to #GstBitWriter without checking whether - * there is enough space - */ -void gst_bit_writer_put_bits_uint8_unchecked (GstBitWriter *bitwriter, guint8 value, guint nbits); - -/** - * gst_bit_writer_put_bits_uint16_unchecked: - * @bitwriter: a #GstBitWriter instance - * @value: value of #guint16 to write - * @nbits: number of bits to write - * - * Write @nbits bits of @value to #GstBitWriter without checking whether - * there is enough space - */ -void gst_bit_writer_put_bits_uint16_unchecked (GstBitWriter *bitwriter, guint16 value, guint nbits); - -/** - * gst_bit_writer_put_bits_uint32_unchecked: - * @bitwriter: a #GstBitWriter instance - * @value: value of #guint32 to write - * @nbits: number of bits to write - * - * Write @nbits bits of @value to #GstBitWriter without checking whether - * there is enough space - */ -void gst_bit_writer_put_bits_uint32_unchecked (GstBitWriter *bitwriter, guint32 value, guint nbits); - -/** - * gst_bit_writer_put_bits_uint64_unchecked: - * @bitwriter: a #GstBitWriter instance - * @value: value of #guint64 to write - * @nbits: number of bits to write - * - * Write @nbits bits of @value to #GstBitWriter without checking whether - * there is enough space - */ -void gst_bit_writer_put_bits_uint64_unchecked (GstBitWriter *bitwriter, guint64 value, guint nbits); - -/** - * gst_bit_writer_put_bytes_unchecked: - * @bitwriter: a #GstBitWriter instance - * @data: pointer of data to write - * @nbytes: number of bytes to write - * - * Write @nbytes bytes of @data to #GstBitWriter without checking whether - * there is enough space - */ -void gst_bit_writer_put_bytes_unchecked (GstBitWriter *bitwriter, const guint8 *data, guint nbytes); - -/** - * gst_bit_writer_align_bytes_unchecked: - * @bitwriter: a #GstBitWriter instance - * @trailing_bit: trailing bits of last byte, 0 or 1 - * - * Write trailing bit to align last byte of @data without checking whether there - * is enough space - */ -void gst_bit_writer_align_bytes_unchecked (GstBitWriter *bitwriter, guint8 trailing_bit); diff --git a/libs/gst/base/gstbitwriter.c b/libs/gst/base/gstbitwriter.c deleted file mode 100644 index 7086c3c3d6..0000000000 --- a/libs/gst/base/gstbitwriter.c +++ /dev/null @@ -1,449 +0,0 @@ -/* - * gstbitwriter.c - bitstream writer - * - * Copyright (C) 2013 Intel Corporation - * Copyright (C) 2018 Igalia, S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 - * 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#define GST_BIT_WRITER_DISABLE_INLINES -#include "gstbitwriter.h" - -#include "gst/glib-compat-private.h" - -/** - * SECTION:gstbitwriter - * @title: GstBitWriter - * @short_description: Writes any number of bits into a memory buffer - * - * #GstBitWriter provides a bit writer that can write any number of - * bits into a memory buffer. It provides functions for writing any - * number of bits into 8, 16, 32 and 64 bit variables. - */ - -/** - * gst_bit_writer_new: (skip) - * - * Creates a new, empty #GstBitWriter instance. - * - * Free-function: gst_bit_writer_free - * - * Returns: (transfer full): a new, empty #GstByteWriter instance - **/ -GstBitWriter * -gst_bit_writer_new (void) -{ - GstBitWriter *ret = g_slice_new0 (GstBitWriter); - - ret->owned = TRUE; - ret->auto_grow = TRUE; - return ret; -} - -/** - * gst_bit_writer_new_with_size: (skip) - * @size: Initial size of data in bytes - * @fixed: If %TRUE the data can't be reallocated - * - * Creates a #GstBitWriter instance with the given initial data size. - * - * Free-function: gst_bit_writer_free - * - * Returns: (transfer full): a new #GstBitWriter instance - */ -GstBitWriter * -gst_bit_writer_new_with_size (guint size, gboolean fixed) -{ - GstBitWriter *ret = g_slice_new0 (GstBitWriter); - - gst_bit_writer_init_with_size (ret, size, fixed); - return ret; -} - -/** - * gst_bit_writer_new_with_data: (skip) - * @data: Memory area for writing - * @size: Size of @data in bytes - * @initialized: if %TRUE the complete data can be read from the beginning - * - * Creates a new #GstBitWriter instance with the given memory area. If - * @initialized is %TRUE it is possible to read @size bits from the - * #GstBitWriter from the beginning. - * - * Free-function: gst_bit_writer_free - * - * Returns: (transfer full): a new #GstBitWriter instance - */ -GstBitWriter * -gst_bit_writer_new_with_data (guint8 * data, guint size, gboolean initialized) -{ - GstBitWriter *ret = g_slice_new0 (GstBitWriter); - - gst_bit_writer_init_with_data (ret, data, size, initialized); - - return ret; -} - -/** - * gst_bit_writer_init: (skip) - * @bitwriter: #GstBitWriter instance - * - * Initializes @bitwriter to an empty instance. - **/ -void -gst_bit_writer_init (GstBitWriter * bitwriter) -{ - g_return_if_fail (bitwriter != NULL); - - memset (bitwriter, 0, sizeof (GstBitWriter)); - bitwriter->owned = TRUE; - bitwriter->auto_grow = TRUE; -} - -/** - * gst_bit_writer_init_with_size: (skip) - * @bitwriter: #GstBitWriter instance - * @size: the size on bytes to allocate for data - * @fixed: If %TRUE the data can't be reallocated - * - * Initializes a #GstBitWriter instance and allocates the given data - * @size. - */ -void -gst_bit_writer_init_with_size (GstBitWriter * bitwriter, guint size, - gboolean fixed) -{ - g_return_if_fail (bitwriter != NULL); - - gst_bit_writer_init (bitwriter); - - _gst_bit_writer_check_remaining (bitwriter, size << 3); - - bitwriter->auto_grow = !fixed; -} - -/** - * gst_bit_writer_init_with_data: (skip) - * @bitwriter: #GstBitWriter instance - * @data: (array length=size) (transfer none): Memory area for writing - * @size: Size of @data in bytes - * @initialized: If %TRUE the complete data can be read from the beginning - * - * Initializes @bitwriter with the given memory area @data. IF - * @initialized is %TRUE it is possible to read @size bits from the - * #GstBitWriter from the beginning. - */ -void -gst_bit_writer_init_with_data (GstBitWriter * bitwriter, guint8 * data, - guint size, gboolean initialized) -{ - g_return_if_fail (bitwriter != NULL); - - gst_bit_writer_init (bitwriter); - - bitwriter->data = data; - bitwriter->bit_capacity = size * 8; - bitwriter->bit_size = (initialized) ? size << 3 : 0; - bitwriter->auto_grow = FALSE; - bitwriter->owned = FALSE; -} - -/** - * gst_bit_writer_reset: - * @bitwriter: #GstBitWriter instance - * - * Resets @bitwriter and frees the data if it's owned by @bitwriter. - */ -void -gst_bit_writer_reset (GstBitWriter * bitwriter) -{ - g_return_if_fail (bitwriter != NULL); - - if (bitwriter->owned) - g_free (bitwriter->data); - memset (bitwriter, 0, sizeof (GstBitWriter)); -} - -/** - * gst_bit_writer_reset_and_get_data: - * @bitwriter: a #GstBitWriter instance - * - * Resets @bitwriter and returns the current data. - * - * Free-function: g_free - * - * Returns: (array) (transfer full): the current data. g_free() after - * usage. - **/ -guint8 * -gst_bit_writer_reset_and_get_data (GstBitWriter * bitwriter) -{ - guint8 *data; - - g_return_val_if_fail (bitwriter != NULL, NULL); - - data = bitwriter->data; - if (bitwriter->owned) - data = g_memdup2 (data, GST_ROUND_UP_8 (bitwriter->bit_size) >> 3); - gst_bit_writer_reset (bitwriter); - - return data; -} - -/** - * gst_bit_writer_reset_and_get_buffer: - * @bitwriter: a #GstBitWriter instance - * - * Resets @bitwriter and returns the current data as #GstBuffer. - * - * Free-function: gst_buffer_unref - * - * Returns: (transfer full): a new allocated #GstBuffer wrapping the - * current data. gst_buffer_unref() after usage. - **/ -GstBuffer * -gst_bit_writer_reset_and_get_buffer (GstBitWriter * bitwriter) -{ - GstBuffer *buffer; - gpointer data; - gsize size; - gboolean owned; - - g_return_val_if_fail (bitwriter != NULL, NULL); - - owned = bitwriter->owned; - - size = GST_ROUND_UP_8 (bitwriter->bit_size) >> 3; - data = gst_bit_writer_reset_and_get_data (bitwriter); - - /* we cannot rely on buffers allocated externally, thus let's dup - * the data */ - if (data && !owned) - data = g_memdup2 (data, size); - - buffer = gst_buffer_new (); - if (data != NULL) { - gst_buffer_append_memory (buffer, - gst_memory_new_wrapped (0, data, size, 0, size, data, g_free)); - } - - return buffer; -} - -/** - * gst_bit_writer_free: - * @bitwriter: (in) (transfer full): #GstBitWriter instance - * - * Frees @bitwriter and the allocated data inside. - */ -void -gst_bit_writer_free (GstBitWriter * bitwriter) -{ - g_return_if_fail (bitwriter != NULL); - - gst_bit_writer_reset (bitwriter); - g_slice_free (GstBitWriter, bitwriter); -} - -/** - * gst_bit_writer_free_and_get_data: - * @bitwriter: (in) (transfer full): #GstBitWriter instance - * - * Frees @bitwriter without destroying the internal data, which is - * returned. - * - * Free-function: g_free - * - * Returns: (array) (transfer full): the current data. g_free() after - * usage. - **/ -guint8 * -gst_bit_writer_free_and_get_data (GstBitWriter * bitwriter) -{ - guint8 *data; - - g_return_val_if_fail (bitwriter != NULL, NULL); - - data = gst_bit_writer_reset_and_get_data (bitwriter); - g_slice_free (GstBitWriter, bitwriter); - - return data; -} - -/** - * gst_bit_writer_free_and_get_buffer: - * @bitwriter: (in) (transfer full): #GstBitWriter instance - * - * Frees @bitwriter without destroying the internal data, which is - * returned as #GstBuffer. - * - * Free-function: gst_buffer_unref - * - * Returns: (transfer full): a new allocated #GstBuffer wrapping the - * data inside. gst_buffer_unref() after usage. - **/ -GstBuffer * -gst_bit_writer_free_and_get_buffer (GstBitWriter * bitwriter) -{ - GstBuffer *buffer; - - g_return_val_if_fail (bitwriter != NULL, NULL); - - buffer = gst_bit_writer_reset_and_get_buffer (bitwriter); - g_slice_free (GstBitWriter, bitwriter); - - return buffer; -} - -/** - * gst_bit_writer_get_size: - * @bitwriter: a #GstBitWriter instance - * - * Get size of written @data - * - * Returns: size of bits written in @data - */ -guint -gst_bit_writer_get_size (const GstBitWriter * bitwriter) -{ - return _gst_bit_writer_get_size_inline (bitwriter); -} - -/** - * gst_bit_writer_get_data: - * @bitwriter: a #GstBitWriter instance - * - * Get written data pointer - * - * Returns: data pointer - */ -guint8 * -gst_bit_writer_get_data (const GstBitWriter * bitwriter) -{ - return _gst_bit_writer_get_data_inline (bitwriter); -} - -/** - * gst_bit_writer_get_pos: - * @bitwriter: a #GstBitWriter instance - * @pos: The new position in bits - * - * Set the new position of data end which should be the new size of @data. - * - * Returns: %TRUE if successful, %FALSE otherwise - */ -gboolean -gst_bit_writer_set_pos (GstBitWriter * bitwriter, guint pos) -{ - return _gst_bit_writer_set_pos_inline (bitwriter, pos); -} - -/** - * gst_bit_writer_put_bits_uint8: - * @bitwriter: a #GstBitWriter instance - * @value: value of #guint8 to write - * @nbits: number of bits to write - * - * Write @nbits bits of @value to #GstBitWriter. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_bit_writer_put_bits_uint16: - * @bitwriter: a #GstBitWriter instance - * @value: value of #guint16 to write - * @nbits: number of bits to write - * - * Write @nbits bits of @value to #GstBitWriter. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_bit_writer_put_bits_uint32: - * @bitwriter: a #GstBitWriter instance - * @value: value of #guint32 to write - * @nbits: number of bits to write - * - * Write @nbits bits of @value to #GstBitWriter. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_bit_writer_put_bits_uint64: - * @bitwriter: a #GstBitWriter instance - * @value: value of #guint64 to write - * @nbits: number of bits to write - * - * Write @nbits bits of @value to #GstBitWriter. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/* *INDENT-OFF* */ -#define GST_BIT_WRITER_WRITE_BITS(bits) \ -gboolean \ -gst_bit_writer_put_bits_uint##bits (GstBitWriter *bitwriter, guint##bits value, guint nbits) \ -{ \ - return _gst_bit_writer_put_bits_uint##bits##_inline (bitwriter, value, nbits); \ -} - -GST_BIT_WRITER_WRITE_BITS (8) -GST_BIT_WRITER_WRITE_BITS (16) -GST_BIT_WRITER_WRITE_BITS (32) -GST_BIT_WRITER_WRITE_BITS (64) -#undef GST_BIT_WRITER_WRITE_BITS -/* *INDENT-ON* */ - -/** - * gst_bit_writer_put_bytes: - * @bitwriter: a #GstBitWriter instance - * @data: pointer of data to write - * @nbytes: number of bytes to write - * - * Write @nbytes bytes of @data to #GstBitWriter. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ -gboolean -gst_bit_writer_put_bytes (GstBitWriter * bitwriter, const guint8 * data, - guint nbytes) -{ - return _gst_bit_writer_put_bytes_inline (bitwriter, data, nbytes); -} - -/** - * gst_bit_writer_align_bytes: - * @bitwriter: a #GstBitWriter instance - * @trailing_bit: trailing bits of last byte, 0 or 1 - * - * Write trailing bit to align last byte of @data. @trailing_bit can - * only be 1 or 0. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ -gboolean -gst_bit_writer_align_bytes (GstBitWriter * bitwriter, guint8 trailing_bit) -{ - return _gst_bit_writer_align_bytes_inline (bitwriter, trailing_bit); -} diff --git a/libs/gst/base/gstbitwriter.h b/libs/gst/base/gstbitwriter.h deleted file mode 100644 index 8a860e8e08..0000000000 --- a/libs/gst/base/gstbitwriter.h +++ /dev/null @@ -1,384 +0,0 @@ -/* - * gstbitwriter.h - bitstream writer - * - * Copyright (C) 2013 Intel Corporation - * Copyright (C) 2018 Igalia, S. L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 - * 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free - * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA - */ - -#ifndef GST_BIT_WRITER_H -#define GST_BIT_WRITER_H - -#include <gst/gst.h> -#include <gst/base/base-prelude.h> - -#include <string.h> - -G_BEGIN_DECLS - -#define GST_BIT_WRITER_DATA(writer) ((writer)->data) -#define GST_BIT_WRITER_BIT_SIZE(writer) ((writer)->bit_size) -#define GST_BIT_WRITER(writer) ((GstBitWriter *) (writer)) - -typedef struct _GstBitWriter GstBitWriter; - -/** - * GstBitWriter: - * @data: Allocated @data for bit writer to write - * @bit_size: Size of written @data in bits - * - * A bit writer instance. - * - * Since: 1.16 - */ -struct _GstBitWriter -{ - guint8 *data; - guint bit_size; - - /*< private >*/ - guint bit_capacity; /* Capacity of the allocated data */ - gboolean auto_grow; /* Whether space can auto grow */ - gboolean owned; - gpointer _gst_reserved[GST_PADDING]; -}; - -GST_BASE_API -GstBitWriter * gst_bit_writer_new (void) G_GNUC_MALLOC; - -GST_BASE_API -GstBitWriter * gst_bit_writer_new_with_size (guint32 size, gboolean fixed) G_GNUC_MALLOC; - -GST_BASE_API -GstBitWriter * gst_bit_writer_new_with_data (guint8 *data, guint size, - gboolean initialized) G_GNUC_MALLOC; - -GST_BASE_API -void gst_bit_writer_free (GstBitWriter *bitwriter); - -GST_BASE_API -guint8 * gst_bit_writer_free_and_get_data (GstBitWriter *bitwriter); - -GST_BASE_API -GstBuffer * gst_bit_writer_free_and_get_buffer (GstBitWriter *bitwriter); - -GST_BASE_API -void gst_bit_writer_init (GstBitWriter *bitwriter); - -GST_BASE_API -void gst_bit_writer_init_with_size (GstBitWriter *bitwriter, - guint32 size, gboolean fixed); - -GST_BASE_API -void gst_bit_writer_init_with_data (GstBitWriter *bitwriter, guint8 *data, - guint size, gboolean initialized); - -GST_BASE_API -void gst_bit_writer_reset (GstBitWriter *bitwriter); - -GST_BASE_API -guint8 * gst_bit_writer_reset_and_get_data (GstBitWriter *bitwriter); - -GST_BASE_API -GstBuffer * gst_bit_writer_reset_and_get_buffer (GstBitWriter *bitwriter); - -GST_BASE_API -guint gst_bit_writer_get_size (const GstBitWriter *bitwriter); - -GST_BASE_API -guint8 * gst_bit_writer_get_data (const GstBitWriter *bitwriter); - -GST_BASE_API -gboolean gst_bit_writer_set_pos (GstBitWriter *bitwriter, guint pos); - -GST_BASE_API -guint gst_bit_writer_get_remaining (const GstBitWriter *bitwriter); - -GST_BASE_API -gboolean gst_bit_writer_put_bits_uint8 (GstBitWriter *bitwriter, guint8 value, - guint nbits); - -GST_BASE_API -gboolean gst_bit_writer_put_bits_uint16 (GstBitWriter *bitwriter, guint16 value, - guint nbits); - -GST_BASE_API -gboolean gst_bit_writer_put_bits_uint32 (GstBitWriter *bitwriter, guint32 value, - guint nbits); - -GST_BASE_API -gboolean gst_bit_writer_put_bits_uint64 (GstBitWriter *bitwriter, guint64 value, - guint nbits); - -GST_BASE_API -gboolean gst_bit_writer_put_bytes (GstBitWriter *bitwriter, const guint8 *data, - guint nbytes); - -GST_BASE_API -gboolean gst_bit_writer_align_bytes (GstBitWriter *bitwriter, guint8 trailing_bit); - -static const guint8 _gst_bit_writer_bit_filling_mask[9] = { - 0x00, 0x01, 0x03, 0x07, - 0x0F, 0x1F, 0x3F, 0x7F, - 0xFF -}; - -/* Aligned to 256 bytes */ -#define __GST_BITS_WRITER_ALIGNMENT_MASK 2047 -#define __GST_BITS_WRITER_ALIGNED(bitsize) \ - (((bitsize) + __GST_BITS_WRITER_ALIGNMENT_MASK)&(~__GST_BITS_WRITER_ALIGNMENT_MASK)) - -static inline gboolean -_gst_bit_writer_check_remaining (GstBitWriter * bitwriter, guint32 bits) -{ - guint32 new_bit_size = bits + bitwriter->bit_size; - guint32 clear_pos; - - g_assert (bitwriter->bit_size <= bitwriter->bit_capacity); - if (new_bit_size <= bitwriter->bit_capacity) - return TRUE; - - if (!bitwriter->auto_grow) - return FALSE; - - /* auto grow space */ - new_bit_size = __GST_BITS_WRITER_ALIGNED (new_bit_size); - g_assert (new_bit_size - && ((new_bit_size & __GST_BITS_WRITER_ALIGNMENT_MASK) == 0)); - clear_pos = ((bitwriter->bit_size + 7) >> 3); - bitwriter->data = (guint8 *) g_realloc (bitwriter->data, (new_bit_size >> 3)); - memset (bitwriter->data + clear_pos, 0, (new_bit_size >> 3) - clear_pos); - bitwriter->bit_capacity = new_bit_size; - return TRUE; -} - -#undef __GST_BITS_WRITER_ALIGNMENT_MASK -#undef __GST_BITS_WRITER_ALIGNED - -#define __GST_BIT_WRITER_WRITE_BITS_UNCHECKED(bits) \ -static inline void \ -gst_bit_writer_put_bits_uint##bits##_unchecked( \ - GstBitWriter *bitwriter, \ - guint##bits value, \ - guint nbits \ -) \ -{ \ - guint byte_pos, bit_offset; \ - guint8 *cur_byte; \ - guint fill_bits; \ - \ - byte_pos = (bitwriter->bit_size >> 3); \ - bit_offset = (bitwriter->bit_size & 0x07); \ - cur_byte = bitwriter->data + byte_pos; \ - g_assert (nbits <= bits); \ - g_assert( bit_offset < 8 && \ - bitwriter->bit_size <= bitwriter->bit_capacity); \ - \ - while (nbits) { \ - fill_bits = ((8 - bit_offset) < nbits ? (8 - bit_offset) : nbits); \ - nbits -= fill_bits; \ - bitwriter->bit_size += fill_bits; \ - \ - *cur_byte |= (((value >> nbits) & _gst_bit_writer_bit_filling_mask[fill_bits]) \ - << (8 - bit_offset - fill_bits)); \ - ++cur_byte; \ - bit_offset = 0; \ - } \ - g_assert(cur_byte <= \ - (bitwriter->data + (bitwriter->bit_capacity >> 3))); \ -} - -__GST_BIT_WRITER_WRITE_BITS_UNCHECKED (8) -__GST_BIT_WRITER_WRITE_BITS_UNCHECKED (16) -__GST_BIT_WRITER_WRITE_BITS_UNCHECKED (32) -__GST_BIT_WRITER_WRITE_BITS_UNCHECKED (64) -#undef __GST_BIT_WRITER_WRITE_BITS_UNCHECKED - -static inline guint -gst_bit_writer_get_size_unchecked (const GstBitWriter * bitwriter) -{ - return GST_BIT_WRITER_BIT_SIZE (bitwriter); -} - -static inline guint8 * -gst_bit_writer_get_data_unchecked (const GstBitWriter * bitwriter) -{ - return GST_BIT_WRITER_DATA (bitwriter); -} - -static inline gboolean -gst_bit_writer_set_pos_unchecked (GstBitWriter * bitwriter, guint pos) -{ - GST_BIT_WRITER_BIT_SIZE (bitwriter) = pos; - return TRUE; -} - -static inline guint -gst_bit_writer_get_remaining_unchecked (const GstBitWriter * bitwriter) -{ - return bitwriter->bit_capacity - bitwriter->bit_size; -} - -static inline void -gst_bit_writer_put_bytes_unchecked (GstBitWriter * bitwriter, - const guint8 * data, guint nbytes) -{ - if ((bitwriter->bit_size & 0x07) == 0) { - memcpy (&bitwriter->data[bitwriter->bit_size >> 3], data, nbytes); - bitwriter->bit_size += (nbytes << 3); - } else { - g_assert (0); - while (nbytes) { - gst_bit_writer_put_bits_uint8_unchecked (bitwriter, *data, 8); - --nbytes; - ++data; - } - } -} - -static inline void -gst_bit_writer_align_bytes_unchecked (GstBitWriter * bitwriter, - guint8 trailing_bit) -{ - guint32 bit_offset, bit_left; - guint8 value = 0; - - bit_offset = (bitwriter->bit_size & 0x07); - if (!bit_offset) - return; - - bit_left = 8 - bit_offset; - if (trailing_bit) - value = _gst_bit_writer_bit_filling_mask[bit_left]; - gst_bit_writer_put_bits_uint8_unchecked (bitwriter, value, bit_left); -} - -#define __GST_BIT_WRITER_WRITE_BITS_INLINE(bits) \ -static inline gboolean \ -_gst_bit_writer_put_bits_uint##bits##_inline( \ - GstBitWriter *bitwriter, \ - guint##bits value, \ - guint nbits \ -) \ -{ \ - g_return_val_if_fail(bitwriter != NULL, FALSE); \ - g_return_val_if_fail(nbits != 0, FALSE); \ - g_return_val_if_fail(nbits <= bits, FALSE); \ - \ - if (!_gst_bit_writer_check_remaining(bitwriter, nbits)) \ - return FALSE; \ - gst_bit_writer_put_bits_uint##bits##_unchecked(bitwriter, value, nbits); \ - return TRUE; \ -} - -__GST_BIT_WRITER_WRITE_BITS_INLINE (8) -__GST_BIT_WRITER_WRITE_BITS_INLINE (16) -__GST_BIT_WRITER_WRITE_BITS_INLINE (32) -__GST_BIT_WRITER_WRITE_BITS_INLINE (64) -#undef __GST_BIT_WRITER_WRITE_BITS_INLINE - -static inline guint -_gst_bit_writer_get_size_inline (const GstBitWriter * bitwriter) -{ - g_return_val_if_fail (bitwriter != NULL, 0); - - return gst_bit_writer_get_size_unchecked (bitwriter); -} - -static inline guint8 * -_gst_bit_writer_get_data_inline (const GstBitWriter * bitwriter) -{ - g_return_val_if_fail (bitwriter != NULL, NULL); - - return gst_bit_writer_get_data_unchecked (bitwriter); -} - -static inline gboolean -_gst_bit_writer_set_pos_inline (GstBitWriter * bitwriter, guint pos) -{ - g_return_val_if_fail (bitwriter != NULL, FALSE); - g_return_val_if_fail (pos <= bitwriter->bit_capacity, FALSE); - - return gst_bit_writer_set_pos_unchecked (bitwriter, pos); -} - -static inline guint -_gst_bit_writer_get_remaining_inline (const GstBitWriter * bitwriter) -{ - g_return_val_if_fail (bitwriter != NULL, 0); - g_return_val_if_fail (bitwriter->bit_size < bitwriter->bit_capacity, 0); - - return gst_bit_writer_get_remaining_unchecked (bitwriter); -} - -static inline gboolean -_gst_bit_writer_put_bytes_inline (GstBitWriter * bitwriter, - const guint8 * data, guint nbytes) -{ - g_return_val_if_fail (bitwriter != NULL, FALSE); - g_return_val_if_fail (data != NULL, FALSE); - g_return_val_if_fail (nbytes, FALSE); - - if (!_gst_bit_writer_check_remaining (bitwriter, nbytes * 8)) - return FALSE; - - gst_bit_writer_put_bytes_unchecked (bitwriter, data, nbytes); - return TRUE; -} - -static inline gboolean -_gst_bit_writer_align_bytes_inline (GstBitWriter * bitwriter, - guint8 trailing_bit) -{ - g_return_val_if_fail (bitwriter != NULL, FALSE); - g_return_val_if_fail ((trailing_bit == 0 || trailing_bit == 1), FALSE); - g_return_val_if_fail (((bitwriter->bit_size + 7) & (~7)) <= - bitwriter->bit_capacity, FALSE); - - gst_bit_writer_align_bytes_unchecked (bitwriter, trailing_bit); - return TRUE; -} - -#ifndef GST_BIT_WRITER_DISABLE_INLINES -#define gst_bit_writer_get_size(bitwriter) \ - _gst_bit_writer_get_size_inline(bitwriter) -#define gst_bit_writer_get_data(bitwriter) \ - _gst_bit_writer_get_data_inline(bitwriter) -#define gst_bit_writer_set_pos(bitwriter, pos) \ - G_LIKELY (_gst_bit_writer_set_pos_inline (bitwriter, pos)) -#define gst_bit_writer_get_remaining(bitwriter) \ - _gst_bit_writer_get_remaining_inline(bitwriter) - -#define gst_bit_writer_put_bits_uint8(bitwriter, value, nbits) \ - G_LIKELY (_gst_bit_writer_put_bits_uint8_inline (bitwriter, value, nbits)) -#define gst_bit_writer_put_bits_uint16(bitwriter, value, nbits) \ - G_LIKELY (_gst_bit_writer_put_bits_uint16_inline (bitwriter, value, nbits)) -#define gst_bit_writer_put_bits_uint32(bitwriter, value, nbits) \ - G_LIKELY (_gst_bit_writer_put_bits_uint32_inline (bitwriter, value, nbits)) -#define gst_bit_writer_put_bits_uint64(bitwriter, value, nbits) \ - G_LIKELY (_gst_bit_writer_put_bits_uint64_inline (bitwriter, value, nbits)) - -#define gst_bit_writer_put_bytes(bitwriter, data, nbytes) \ - G_LIKELY (_gst_bit_writer_put_bytes_inline (bitwriter, data, nbytes)) - -#define gst_bit_writer_align_bytes(bitwriter, trailing_bit) \ - G_LIKELY (_gst_bit_writer_align_bytes_inline(bitwriter, trailing_bit)) -#endif - -G_END_DECLS - -#endif /* GST_BIT_WRITER_H */ diff --git a/libs/gst/base/gstbytereader-docs.h b/libs/gst/base/gstbytereader-docs.h deleted file mode 100644 index b582c3f744..0000000000 --- a/libs/gst/base/gstbytereader-docs.h +++ /dev/null @@ -1,552 +0,0 @@ -/* GStreamer byte reader dummy header for gtk-doc - * Copyright (C) 2009 Tim-Philipp Müller <tim centricular net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/* This header is not installed, it just contains stuff for gtk-doc to parse, - * in particular docs and some dummy function declarations for the static - * inline functions we generate via macros in gstbytereader.h. - */ - -#error "This header should never be included in code, it is only for gtk-doc" - -/** - * gst_byte_reader_skip_unchecked: - * @reader: a #GstByteReader instance - * @nbytes: the number of bytes to skip - * - * Skips @nbytes bytes of the #GstByteReader instance without checking if - * there are enough bytes available in the byte reader. - */ -void gst_byte_reader_skip_unchecked (GstByteReader * reader, guint nbytes); - -/** - * gst_byte_reader_get_uint8_unchecked: - * @reader: a #GstByteReader instance - * - * Read an unsigned 8 bit integer without checking if there are enough bytes - * available in the byte reader and update the current position. - * - * Returns: unsigned 8 bit integer. - */ -/** - * gst_byte_reader_peek_uint8_unchecked: - * @reader: a #GstByteReader instance - * - * Read an unsigned 8 bit integer without checking if there are enough bytes - * available in the byte reader, but do not advance the current read position. - * - * Returns: unsigned 8 bit integer. - */ -/** - * gst_byte_reader_get_int8_unchecked: - * @reader: a #GstByteReader instance - * - * Read an signed 8 bit integer without checking if there are enough bytes - * available in the byte reader and update the current position. - * - * Returns: signed 8 bit integer. - */ -/** - * gst_byte_reader_peek_int8_unchecked: - * @reader: a #GstByteReader instance - * - * Read an signed 8 bit integer without checking if there are enough bytes - * available in the byte reader, but do not advance the current read position. - * - * Returns: signed 8 bit integer. - */ -guint8 gst_byte_reader_get_uint8_unchecked (GstByteReader * reader); -guint8 gst_byte_reader_peek_uint8_unchecked (GstByteReader * reader); -gint8 gst_byte_reader_get_int8_unchecked (GstByteReader * reader); -gint8 gst_byte_reader_peek_int8_unchecked (GstByteReader * reader); - -/** - * gst_byte_reader_get_uint16_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read an unsigned 16 bit integer in little endian format without checking - * if there are enough bytes available in the byte reader and update the - * current position. - * - * Returns: unsigned 16 bit integer. - */ -/** - * gst_byte_reader_peek_uint16_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read an unsigned 16 bit integer in little endian format without checking - * if there are enough bytes available in the byte reader, but do not advance - * the current position. - * - * Returns: unsigned 16 bit integer. - */ -/** - * gst_byte_reader_get_uint16_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read an unsigned 16 bit integer in big endian format without checking - * if there are enough bytes available in the byte reader and update the - * current position. - * - * Returns: unsigned 16 bit integer. - */ -/** - * gst_byte_reader_peek_uint16_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read an unsigned 16 bit integer in big endian format without checking - * if there are enough bytes available in the byte reader, but do not advance - * the current position. - * - * Returns: unsigned 16 bit integer. - */ -/** - * gst_byte_reader_get_int16_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read a signed 16 bit integer in little endian format without checking - * if there are enough bytes available in the byte reader and update the - * current position. - * - * Returns: signed 16 bit integer. - */ -/** - * gst_byte_reader_peek_int16_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read a signed 16 bit integer in little endian format without checking - * if there are enough bytes available in the byte reader, but do not advance - * the current position. - * - * Returns: signed 16 bit integer. - */ -/** - * gst_byte_reader_get_int16_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read a signed 16 bit integer in big endian format without checking - * if there are enough bytes available in the byte reader and update the - * current position. - * - * Returns: signed 16 bit integer. - */ -/** - * gst_byte_reader_peek_int16_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read a signed 16 bit integer in big endian format without checking - * if there are enough bytes available in the byte reader, but do not advance - * the current position. - * - * Returns: signed 16 bit integer. - */ -guint16 gst_byte_reader_get_uint16_le_unchecked (GstByteReader * reader); -guint16 gst_byte_reader_get_uint16_be_unchecked (GstByteReader * reader); -guint16 gst_byte_reader_peek_uint16_le_unchecked (GstByteReader * reader); -guint16 gst_byte_reader_peek_uint16_be_unchecked (GstByteReader * reader); -gint16 gst_byte_reader_get_int16_le_unchecked (GstByteReader * reader); -gint16 gst_byte_reader_get_int16_be_unchecked (GstByteReader * reader); -gint16 gst_byte_reader_peek_int16_le_unchecked (GstByteReader * reader); -gint16 gst_byte_reader_peek_int16_be_unchecked (GstByteReader * reader); - -/** - * gst_byte_reader_get_uint24_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read an unsigned 24 bit integer in little endian format without checking - * if there are enough bytes available in the byte reader and update the - * current position. - * - * Returns: unsigned 24 bit integer (as guint32) - */ -/** - * gst_byte_reader_peek_uint24_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read an unsigned 24 bit integer in little endian format without checking - * if there are enough bytes available in the byte reader, but do not advance - * the current position. - * - * Returns: unsigned 24 bit integer (as guint32) - */ -/** - * gst_byte_reader_get_uint24_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read an unsigned 24 bit integer in big endian format without checking - * if there are enough bytes available in the byte reader and update the - * current position. - * - * Returns: unsigned 24 bit integer (as guint32) - */ -/** - * gst_byte_reader_peek_uint24_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read an unsigned 24 bit integer in big endian format without checking - * if there are enough bytes available in the byte reader, but do not advance - * the current position. - * - * Returns: unsigned 24 bit integer (as guint32) - */ -/** - * gst_byte_reader_get_int24_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read a signed 24 bit integer in little endian format without checking - * if there are enough bytes available in the byte reader and update the - * current position. - * - * Returns: signed 24 bit integer (as gint32) - */ -/** - * gst_byte_reader_peek_int24_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read a signed 24 bit integer in little endian format without checking - * if there are enough bytes available in the byte reader, but do not advance - * the current position. - * - * Returns: signed 24 bit integer (as gint32) - */ -/** - * gst_byte_reader_get_int24_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read a signed 24 bit integer in big endian format without checking - * if there are enough bytes available in the byte reader and update the - * current position. - * - * Returns: signed 24 bit integer (as gint32) - */ -/** - * gst_byte_reader_peek_int24_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read a signed 24 bit integer in big endian format without checking - * if there are enough bytes available in the byte reader, but do not advance - * the current position. - * - * Returns: signed 24 bit integer (as gint32) - */ -guint32 gst_byte_reader_get_uint24_le_unchecked (GstByteReader * reader); -guint32 gst_byte_reader_get_uint24_be_unchecked (GstByteReader * reader); -guint32 gst_byte_reader_peek_uint24_le_unchecked (GstByteReader * reader); -guint32 gst_byte_reader_peek_uint24_be_unchecked (GstByteReader * reader); -gint32 gst_byte_reader_get_int24_le_unchecked (GstByteReader * reader); -gint32 gst_byte_reader_get_int24_be_unchecked (GstByteReader * reader); -gint32 gst_byte_reader_peek_int24_le_unchecked (GstByteReader * reader); -gint32 gst_byte_reader_peek_int24_be_unchecked (GstByteReader * reader); - -/** - * gst_byte_reader_get_uint32_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read an unsigned 32 bit integer in little endian format without checking - * if there are enough bytes available in the byte reader and update the - * current position. - * - * Returns: unsigned 32 bit integer. - */ -/** - * gst_byte_reader_peek_uint32_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read an unsigned 32 bit integer in little endian format without checking - * if there are enough bytes available in the byte reader, but do not advance - * the current position. - * - * Returns: unsigned 32 bit integer. - */ -/** - * gst_byte_reader_get_uint32_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read an unsigned 32 bit integer in big endian format without checking - * if there are enough bytes available in the byte reader and update the - * current position. - * - * Returns: unsigned 32 bit integer. - */ -/** - * gst_byte_reader_peek_uint32_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read an unsigned 32 bit integer in big endian format without checking - * if there are enough bytes available in the byte reader, but do not advance - * the current position. - * - * Returns: unsigned 32 bit integer. - */ -/** - * gst_byte_reader_get_int32_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read a signed 32 bit integer in little endian format without checking - * if there are enough bytes available in the byte reader and update the - * current position. - * - * Returns: signed 32 bit integer. - */ -/** - * gst_byte_reader_peek_int32_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read a signed 32 bit integer in little endian format without checking - * if there are enough bytes available in the byte reader, but do not advance - * the current position. - * - * Returns: signed 32 bit integer. - */ -/** - * gst_byte_reader_get_int32_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read a signed 32 bit integer in big endian format without checking - * if there are enough bytes available in the byte reader and update the - * current position. - * - * Returns: signed 32 bit integer. - */ -/** - * gst_byte_reader_peek_int32_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read a signed 32 bit integer in big endian format without checking - * if there are enough bytes available in the byte reader, but do not advance - * the current position. - * - * Returns: signed 32 bit integer. - */ -guint32 gst_byte_reader_get_uint32_le_unchecked (GstByteReader * reader); -guint32 gst_byte_reader_get_uint32_be_unchecked (GstByteReader * reader); -guint32 gst_byte_reader_peek_uint32_le_unchecked (GstByteReader * reader); -guint32 gst_byte_reader_peek_uint32_be_unchecked (GstByteReader * reader); -gint32 gst_byte_reader_get_int32_le_unchecked (GstByteReader * reader); -gint32 gst_byte_reader_get_int32_be_unchecked (GstByteReader * reader); -gint32 gst_byte_reader_peek_int32_le_unchecked (GstByteReader * reader); -gint32 gst_byte_reader_peek_int32_be_unchecked (GstByteReader * reader); - -/** - * gst_byte_reader_get_uint64_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read an unsigned 64 bit integer in little endian format without checking - * if there are enough bytes available in the byte reader and update the - * current position. - * - * Returns: unsigned 64 bit integer. - */ -/** - * gst_byte_reader_peek_uint64_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read an unsigned 64 bit integer in little endian format without checking - * if there are enough bytes available in the byte reader, but do not advance - * the current position. - * - * Returns: unsigned 64 bit integer. - */ -/** - * gst_byte_reader_get_uint64_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read an unsigned 64 bit integer in big endian format without checking - * if there are enough bytes available in the byte reader and update the - * current position. - * - * Returns: unsigned 64 bit integer. - */ -/** - * gst_byte_reader_peek_uint64_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read an unsigned 64 bit integer in big endian format without checking - * if there are enough bytes available in the byte reader, but do not advance - * the current position. - * - * Returns: unsigned 64 bit integer. - */ -/** - * gst_byte_reader_get_int64_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read a signed 64 bit integer in little endian format without checking - * if there are enough bytes available in the byte reader and update the - * current position. - * - * Returns: signed 64 bit integer. - */ -/** - * gst_byte_reader_peek_int64_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read a signed 64 bit integer in little endian format without checking - * if there are enough bytes available in the byte reader, but do not advance - * the current position. - * - * Returns: signed 64 bit integer. - */ -/** - * gst_byte_reader_get_int64_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read a signed 64 bit integer in big endian format without checking - * if there are enough bytes available in the byte reader and update the - * current position. - * - * Returns: signed 64 bit integer. - */ -/** - * gst_byte_reader_peek_int64_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read a signed 64 bit integer in big endian format without checking - * if there are enough bytes available in the byte reader, but do not advance - * the current position. - * - * Returns: signed 64 bit integer. - */ -guint64 gst_byte_reader_get_uint64_le_unchecked (GstByteReader * reader); -guint64 gst_byte_reader_get_uint64_be_unchecked (GstByteReader * reader); -guint64 gst_byte_reader_peek_uint64_le_unchecked (GstByteReader * reader); -guint64 gst_byte_reader_peek_uint64_be_unchecked (GstByteReader * reader); -gint64 gst_byte_reader_get_int64_le_unchecked (GstByteReader * reader); -gint64 gst_byte_reader_get_int64_be_unchecked (GstByteReader * reader); -gint64 gst_byte_reader_peek_int64_le_unchecked (GstByteReader * reader); -gint64 gst_byte_reader_peek_int64_be_unchecked (GstByteReader * reader); - -/** - * gst_byte_reader_get_float32_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read a 32 bit little endian float without checking if there is enough - * data available and update the current position. - * - * Returns: floating point value read - */ -/** - * gst_byte_reader_peek_float32_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read a 32 bit little endian float without checking if there is enough - * data available, but keep the current position. - * - * Returns: floating point value read - */ -/** - * gst_byte_reader_get_float32_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read a 32 bit big endian float without checking if there is enough - * data available and update the current position. - * - * Returns: floating point value read - */ -/** - * gst_byte_reader_peek_float32_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read a 32 bit big endian float without checking if there is enough - * data available, but keep the current position. - * - * Returns: floating point value read - */ -/** - * gst_byte_reader_get_float64_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read a 64 bit little endian float without checking if there is enough - * data available and update the current position. - * - * Returns: double precision floating point value read - */ -/** - * gst_byte_reader_peek_float64_le_unchecked: - * @reader: a #GstByteReader instance - * - * Read a 64 bit little endian float without checking if there is enough - * data available, but keep the current position. - * - * Returns: double precision floating point value read - */ -/** - * gst_byte_reader_get_float64_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read a 64 bit big endian float without checking if there is enough - * data available and update the current position. - * - * Returns: double precision floating point value read - */ -/** - * gst_byte_reader_peek_float64_be_unchecked: - * @reader: a #GstByteReader instance - * - * Read a 64 bit big endian float without checking if there is enough - * data available, but keep the current position. - * - * Returns: double precision floating point value read - */ - -gfloat gst_byte_reader_get_float32_le_unchecked (GstByteReader * reader); -gfloat gst_byte_reader_get_float32_be_unchecked (GstByteReader * reader); -gdouble gst_byte_reader_get_float64_le_unchecked (GstByteReader * reader); -gdouble gst_byte_reader_get_float64_be_unchecked (GstByteReader * reader); - -gfloat gst_byte_reader_peek_float32_le_unchecked (GstByteReader * reader); -gfloat gst_byte_reader_peek_float32_be_unchecked (GstByteReader * reader); -gdouble gst_byte_reader_peek_float64_le_unchecked (GstByteReader * reader); -gdouble gst_byte_reader_peek_float64_be_unchecked (GstByteReader * reader); - -/** - * gst_byte_reader_peek_data_unchecked: - * @reader: a #GstByteReader instance - * - * Returns: (transfer none): a constant pointer to the current data position - */ -const guint8 * gst_byte_reader_peek_data_unchecked (const GstByteReader * reader); -/** - * gst_byte_reader_get_data_unchecked: - * @reader: a #GstByteReader instance - * @size: Size in bytes - * - * Returns a constant pointer to the current data position without checking - * if at least @size bytes are left. Advances the current read position by - * @size bytes. - * - * Returns: (transfer none) (array length=size): a constant pointer to the - * current data position. - */ -const guint8 * gst_byte_reader_get_data_unchecked (GstByteReader * reader, guint size); -/** - * gst_byte_reader_dup_data_unchecked: - * @reader: a #GstByteReader instance - * @size: Size in bytes - * - * Returns a newly-allocated copy of the data at the current data position - * without checking if at least @size bytes are left. Advances the current read - * position by @size bytes. - * - * Free-function: g_free - * - * Returns: (transfer full) (array length=size): a newly-allocated copy of the - * data @size bytes in size. Free with g_free() when no longer needed. - */ -guint8 * gst_byte_reader_dup_data_unchecked (GstByteReader * reader, guint size); - diff --git a/libs/gst/base/gstbytereader.c b/libs/gst/base/gstbytereader.c deleted file mode 100644 index c0a16efe3a..0000000000 --- a/libs/gst/base/gstbytereader.c +++ /dev/null @@ -1,1301 +0,0 @@ -/* GStreamer byte reader - * - * Copyright (C) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>. - * Copyright (C) 2009,2014 Tim-Philipp Müller <tim centricular net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#define GST_BYTE_READER_DISABLE_INLINES -#include "gstbytereader.h" - -#include "gst/glib-compat-private.h" -#include <string.h> - -/** - * SECTION:gstbytereader - * @title: GstByteReader - * @short_description: Reads different integer, string and floating point - * types from a memory buffer - * @symbols: - * - gst_byte_reader_skip_unchecked - * - gst_byte_reader_get_uint8_unchecked - * - gst_byte_reader_peek_uint8_unchecked - * - gst_byte_reader_get_int8_unchecked - * - gst_byte_reader_peek_int8_unchecked - * - gst_byte_reader_get_uint16_le_unchecked - * - gst_byte_reader_get_uint16_be_unchecked - * - gst_byte_reader_peek_uint16_le_unchecked - * - gst_byte_reader_peek_uint16_be_unchecked - * - gst_byte_reader_get_int16_le_unchecked - * - gst_byte_reader_get_int16_be_unchecked - * - gst_byte_reader_peek_int16_le_unchecked - * - gst_byte_reader_peek_int16_be_unchecked - * - gst_byte_reader_get_uint24_le_unchecked - * - gst_byte_reader_get_uint24_be_unchecked - * - gst_byte_reader_peek_uint24_le_unchecked - * - gst_byte_reader_peek_uint24_be_unchecked - * - gst_byte_reader_get_int24_le_unchecked - * - gst_byte_reader_get_int24_be_unchecked - * - gst_byte_reader_peek_int24_le_unchecked - * - gst_byte_reader_peek_int24_be_unchecked - * - gst_byte_reader_get_uint32_le_unchecked - * - gst_byte_reader_get_uint32_be_unchecked - * - gst_byte_reader_peek_uint32_le_unchecked - * - gst_byte_reader_peek_uint32_be_unchecked - * - gst_byte_reader_get_int32_le_unchecked - * - gst_byte_reader_get_int32_be_unchecked - * - gst_byte_reader_peek_int32_le_unchecked - * - gst_byte_reader_peek_int32_be_unchecked - * - gst_byte_reader_get_float32_le_unchecked - * - gst_byte_reader_get_float32_be_unchecked - * - gst_byte_reader_get_float64_le_unchecked - * - gst_byte_reader_get_float64_be_unchecked - * - gst_byte_reader_peek_float32_le_unchecked - * - gst_byte_reader_peek_float32_be_unchecked - * - gst_byte_reader_peek_float64_le_unchecked - * - gst_byte_reader_peek_float64_be_unchecked - * - gst_byte_reader_peek_data_unchecked - * - gst_byte_reader_get_data_unchecked - * - gst_byte_reader_dup_data_unchecked - * - * #GstByteReader provides a byte reader that can read different integer and - * floating point types from a memory buffer. It provides functions for reading - * signed/unsigned, little/big endian integers of 8, 16, 24, 32 and 64 bits - * and functions for reading little/big endian floating points numbers of - * 32 and 64 bits. It also provides functions to read NUL-terminated strings - * in various character encodings. - */ - -/** - * gst_byte_reader_new: (skip) - * @data: (in) (transfer none) (array length=size): data from which the - * #GstByteReader should read - * @size: Size of @data in bytes - * - * Create a new #GstByteReader instance, which will read from @data. - * - * Free-function: gst_byte_reader_free - * - * Returns: (transfer full): a new #GstByteReader instance - */ -GstByteReader * -gst_byte_reader_new (const guint8 * data, guint size) -{ - GstByteReader *ret = g_slice_new0 (GstByteReader); - - ret->data = data; - ret->size = size; - - return ret; -} - -/** - * gst_byte_reader_free: - * @reader: (in) (transfer full): a #GstByteReader instance - * - * Frees a #GstByteReader instance, which was previously allocated by - * gst_byte_reader_new(). - */ -void -gst_byte_reader_free (GstByteReader * reader) -{ - g_return_if_fail (reader != NULL); - - g_slice_free (GstByteReader, reader); -} - -/** - * gst_byte_reader_init: - * @reader: a #GstByteReader instance - * @data: (in) (transfer none) (array length=size): data from which - * the #GstByteReader should read - * @size: Size of @data in bytes - * - * Initializes a #GstByteReader instance to read from @data. This function - * can be called on already initialized instances. - */ -void -gst_byte_reader_init (GstByteReader * reader, const guint8 * data, guint size) -{ - g_return_if_fail (reader != NULL); - - reader->data = data; - reader->size = size; - reader->byte = 0; -} - -/** - * gst_byte_reader_peek_sub_reader: (skip) - * @reader: an existing and initialized #GstByteReader instance - * @sub_reader: a #GstByteReader instance to initialize as sub-reader - * @size: size of @sub_reader in bytes - * - * Initializes a #GstByteReader sub-reader instance to contain @size bytes of - * data from the current position of @reader. This is useful to read chunked - * formats and make sure that one doesn't read beyond the size of the sub-chunk. - * - * Unlike gst_byte_reader_get_sub_reader(), this function does not modify the - * current position of @reader. - * - * Returns: FALSE on error or if @reader does not contain @size more bytes from - * the current position, and otherwise TRUE - * - * Since: 1.6 - */ -gboolean -gst_byte_reader_peek_sub_reader (GstByteReader * reader, - GstByteReader * sub_reader, guint size) -{ - return _gst_byte_reader_peek_sub_reader_inline (reader, sub_reader, size); -} - -/** - * gst_byte_reader_get_sub_reader: (skip) - * @reader: an existing and initialized #GstByteReader instance - * @sub_reader: a #GstByteReader instance to initialize as sub-reader - * @size: size of @sub_reader in bytes - * - * Initializes a #GstByteReader sub-reader instance to contain @size bytes of - * data from the current position of @reader. This is useful to read chunked - * formats and make sure that one doesn't read beyond the size of the sub-chunk. - * - * Unlike gst_byte_reader_peek_sub_reader(), this function also modifies the - * position of @reader and moves it forward by @size bytes. - * - * Returns: FALSE on error or if @reader does not contain @size more bytes from - * the current position, and otherwise TRUE - * - * Since: 1.6 - */ -gboolean -gst_byte_reader_get_sub_reader (GstByteReader * reader, - GstByteReader * sub_reader, guint size) -{ - return _gst_byte_reader_get_sub_reader_inline (reader, sub_reader, size); -} - -/** - * gst_byte_reader_set_pos: - * @reader: a #GstByteReader instance - * @pos: The new position in bytes - * - * Sets the new position of a #GstByteReader instance to @pos in bytes. - * - * Returns: %TRUE if the position could be set successfully, %FALSE - * otherwise. - */ -gboolean -gst_byte_reader_set_pos (GstByteReader * reader, guint pos) -{ - g_return_val_if_fail (reader != NULL, FALSE); - - if (pos > reader->size) - return FALSE; - - reader->byte = pos; - - return TRUE; -} - -/** - * gst_byte_reader_get_pos: - * @reader: a #GstByteReader instance - * - * Returns the current position of a #GstByteReader instance in bytes. - * - * Returns: The current position of @reader in bytes. - */ -guint -gst_byte_reader_get_pos (const GstByteReader * reader) -{ - return _gst_byte_reader_get_pos_inline (reader); -} - -/** - * gst_byte_reader_get_remaining: - * @reader: a #GstByteReader instance - * - * Returns the remaining number of bytes of a #GstByteReader instance. - * - * Returns: The remaining number of bytes of @reader instance. - */ -guint -gst_byte_reader_get_remaining (const GstByteReader * reader) -{ - return _gst_byte_reader_get_remaining_inline (reader); -} - -/** - * gst_byte_reader_get_size: - * @reader: a #GstByteReader instance - * - * Returns the total number of bytes of a #GstByteReader instance. - * - * Returns: The total number of bytes of @reader instance. - */ -guint -gst_byte_reader_get_size (const GstByteReader * reader) -{ - return _gst_byte_reader_get_size_inline (reader); -} - -#define gst_byte_reader_get_remaining _gst_byte_reader_get_remaining_inline -#define gst_byte_reader_get_size _gst_byte_reader_get_size_inline - -/** - * gst_byte_reader_skip: - * @reader: a #GstByteReader instance - * @nbytes: the number of bytes to skip - * - * Skips @nbytes bytes of the #GstByteReader instance. - * - * Returns: %TRUE if @nbytes bytes could be skipped, %FALSE otherwise. - */ -gboolean -gst_byte_reader_skip (GstByteReader * reader, guint nbytes) -{ - return _gst_byte_reader_skip_inline (reader, nbytes); -} - -/** - * gst_byte_reader_get_uint8: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #guint8 to store the result - * - * Read an unsigned 8 bit integer into @val and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_get_int8: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gint8 to store the result - * - * Read a signed 8 bit integer into @val and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_uint8: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #guint8 to store the result - * - * Read an unsigned 8 bit integer into @val but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_int8: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gint8 to store the result - * - * Read a signed 8 bit integer into @val but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_get_uint16_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #guint16 to store the result - * - * Read an unsigned 16 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_get_int16_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gint16 to store the result - * - * Read a signed 16 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_uint16_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #guint16 to store the result - * - * Read an unsigned 16 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_int16_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gint16 to store the result - * - * Read a signed 16 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_get_uint16_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #guint16 to store the result - * - * Read an unsigned 16 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_get_int16_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gint16 to store the result - * - * Read a signed 16 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_uint16_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #guint16 to store the result - * - * Read an unsigned 16 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_int16_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gint16 to store the result - * - * Read a signed 16 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_get_uint24_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #guint32 to store the result - * - * Read an unsigned 24 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_get_int24_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gint32 to store the result - * - * Read a signed 24 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_uint24_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #guint32 to store the result - * - * Read an unsigned 24 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_int24_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gint32 to store the result - * - * Read a signed 24 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_get_uint24_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #guint32 to store the result - * - * Read an unsigned 24 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_get_int24_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gint32 to store the result - * - * Read a signed 24 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_uint24_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #guint32 to store the result - * - * Read an unsigned 24 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_int24_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gint32 to store the result - * - * Read a signed 24 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - - -/** - * gst_byte_reader_get_uint32_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #guint32 to store the result - * - * Read an unsigned 32 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_get_int32_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gint32 to store the result - * - * Read a signed 32 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_uint32_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #guint32 to store the result - * - * Read an unsigned 32 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_int32_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gint32 to store the result - * - * Read a signed 32 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_get_uint32_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #guint32 to store the result - * - * Read an unsigned 32 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_get_int32_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gint32 to store the result - * - * Read a signed 32 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_uint32_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #guint32 to store the result - * - * Read an unsigned 32 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_int32_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gint32 to store the result - * - * Read a signed 32 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_get_uint64_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #guint64 to store the result - * - * Read an unsigned 64 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_get_int64_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gint64 to store the result - * - * Read a signed 64 bit little endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_uint64_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #guint64 to store the result - * - * Read an unsigned 64 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_int64_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gint64 to store the result - * - * Read a signed 64 bit little endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_get_uint64_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #guint64 to store the result - * - * Read an unsigned 64 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_get_int64_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gint64 to store the result - * - * Read a signed 64 bit big endian integer into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_uint64_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #guint64 to store the result - * - * Read an unsigned 64 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_int64_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gint64 to store the result - * - * Read a signed 64 bit big endian integer into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -#define GST_BYTE_READER_PEEK_GET(bits,type,name) \ -gboolean \ -gst_byte_reader_get_##name (GstByteReader * reader, type * val) \ -{ \ - return _gst_byte_reader_get_##name##_inline (reader, val); \ -} \ -\ -gboolean \ -gst_byte_reader_peek_##name (const GstByteReader * reader, type * val) \ -{ \ - return _gst_byte_reader_peek_##name##_inline (reader, val); \ -} - -/* *INDENT-OFF* */ - -GST_BYTE_READER_PEEK_GET(8,guint8,uint8) -GST_BYTE_READER_PEEK_GET(8,gint8,int8) - -GST_BYTE_READER_PEEK_GET(16,guint16,uint16_le) -GST_BYTE_READER_PEEK_GET(16,guint16,uint16_be) -GST_BYTE_READER_PEEK_GET(16,gint16,int16_le) -GST_BYTE_READER_PEEK_GET(16,gint16,int16_be) - -GST_BYTE_READER_PEEK_GET(24,guint32,uint24_le) -GST_BYTE_READER_PEEK_GET(24,guint32,uint24_be) -GST_BYTE_READER_PEEK_GET(24,gint32,int24_le) -GST_BYTE_READER_PEEK_GET(24,gint32,int24_be) - -GST_BYTE_READER_PEEK_GET(32,guint32,uint32_le) -GST_BYTE_READER_PEEK_GET(32,guint32,uint32_be) -GST_BYTE_READER_PEEK_GET(32,gint32,int32_le) -GST_BYTE_READER_PEEK_GET(32,gint32,int32_be) - -GST_BYTE_READER_PEEK_GET(64,guint64,uint64_le) -GST_BYTE_READER_PEEK_GET(64,guint64,uint64_be) -GST_BYTE_READER_PEEK_GET(64,gint64,int64_le) -GST_BYTE_READER_PEEK_GET(64,gint64,int64_be) - -/** - * gst_byte_reader_get_float32_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gfloat to store the result - * - * Read a 32 bit little endian floating point value into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_float32_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gfloat to store the result - * - * Read a 32 bit little endian floating point value into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_get_float32_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gfloat to store the result - * - * Read a 32 bit big endian floating point value into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_float32_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gfloat to store the result - * - * Read a 32 bit big endian floating point value into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_get_float64_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gdouble to store the result - * - * Read a 64 bit little endian floating point value into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_float64_le: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gdouble to store the result - * - * Read a 64 bit little endian floating point value into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_get_float64_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gdouble to store the result - * - * Read a 64 bit big endian floating point value into @val - * and update the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -/** - * gst_byte_reader_peek_float64_be: - * @reader: a #GstByteReader instance - * @val: (out): Pointer to a #gdouble to store the result - * - * Read a 64 bit big endian floating point value into @val - * but keep the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ - -GST_BYTE_READER_PEEK_GET(32,gfloat,float32_le) -GST_BYTE_READER_PEEK_GET(32,gfloat,float32_be) -GST_BYTE_READER_PEEK_GET(64,gdouble,float64_le) -GST_BYTE_READER_PEEK_GET(64,gdouble,float64_be) - -/* *INDENT-ON* */ - -/** - * gst_byte_reader_get_data: - * @reader: a #GstByteReader instance - * @size: Size in bytes - * @val: (out) (transfer none) (array length=size): address of a - * #guint8 pointer variable in which to store the result - * - * Returns a constant pointer to the current data - * position if at least @size bytes are left and - * updates the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ -gboolean -gst_byte_reader_get_data (GstByteReader * reader, guint size, - const guint8 ** val) -{ - return _gst_byte_reader_get_data_inline (reader, size, val); -} - -/** - * gst_byte_reader_peek_data: - * @reader: a #GstByteReader instance - * @size: Size in bytes - * @val: (out) (transfer none) (array length=size): address of a - * #guint8 pointer variable in which to store the result - * - * Returns a constant pointer to the current data - * position if at least @size bytes are left and - * keeps the current position. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ -gboolean -gst_byte_reader_peek_data (const GstByteReader * reader, guint size, - const guint8 ** val) -{ - return _gst_byte_reader_peek_data_inline (reader, size, val); -} - -/** - * gst_byte_reader_dup_data: - * @reader: a #GstByteReader instance - * @size: Size in bytes - * @val: (out) (transfer full) (array length=size): address of a - * #guint8 pointer variable in which to store the result - * - * Free-function: g_free - * - * Returns a newly-allocated copy of the current data - * position if at least @size bytes are left and - * updates the current position. Free with g_free() when no longer needed. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ -gboolean -gst_byte_reader_dup_data (GstByteReader * reader, guint size, guint8 ** val) -{ - return _gst_byte_reader_dup_data_inline (reader, size, val); -} - -/* Special optimized scan for mask 0xffffff00 and pattern 0x00000100 */ -static inline gint -_scan_for_start_code (const guint8 * data, guint size) -{ - guint8 *pdata = (guint8 *) data; - guint8 *pend = (guint8 *) (data + size - 4); - - while (pdata <= pend) { - if (pdata[2] > 1) { - pdata += 3; - } else if (pdata[1]) { - pdata += 2; - } else if (pdata[0] || pdata[2] != 1) { - pdata++; - } else { - return (pdata - data); - } - } - - /* nothing found */ - return -1; -} - -static inline guint -_masked_scan_uint32_peek (const GstByteReader * reader, - guint32 mask, guint32 pattern, guint offset, guint size, guint32 * value) -{ - const guint8 *data; - guint32 state; - guint i; - - g_return_val_if_fail (size > 0, -1); - g_return_val_if_fail ((guint64) offset + size <= reader->size - reader->byte, - -1); - - /* we can't find the pattern with less than 4 bytes */ - if (G_UNLIKELY (size < 4)) - return -1; - - data = reader->data + reader->byte + offset; - - /* Handle special case found in MPEG and H264 */ - if ((pattern == 0x00000100) && (mask == 0xffffff00)) { - gint ret = _scan_for_start_code (data, size); - - if (ret == -1) - return ret; - - if (value != NULL) - *value = (1 << 8) | data[ret + 3]; - - return ret + offset; - } - - /* set the state to something that does not match */ - state = ~pattern; - - /* now find data */ - for (i = 0; i < size; i++) { - /* throw away one byte and move in the next byte */ - state = ((state << 8) | data[i]); - if (G_UNLIKELY ((state & mask) == pattern)) { - /* we have a match but we need to have skipped at - * least 4 bytes to fill the state. */ - if (G_LIKELY (i >= 3)) { - if (value) - *value = state; - return offset + i - 3; - } - } - } - - /* nothing found */ - return -1; -} - - -/** - * gst_byte_reader_masked_scan_uint32: - * @reader: a #GstByteReader - * @mask: mask to apply to data before matching against @pattern - * @pattern: pattern to match (after mask is applied) - * @offset: offset from which to start scanning, relative to the current - * position - * @size: number of bytes to scan from offset - * - * Scan for pattern @pattern with applied mask @mask in the byte reader data, - * starting from offset @offset relative to the current position. - * - * The bytes in @pattern and @mask are interpreted left-to-right, regardless - * of endianness. All four bytes of the pattern must be present in the - * byte reader data for it to match, even if the first or last bytes are masked - * out. - * - * It is an error to call this function without making sure that there is - * enough data (offset+size bytes) in the byte reader. - * - * Returns: offset of the first match, or -1 if no match was found. - * - * Example: - * |[ - * // Assume the reader contains 0x00 0x01 0x02 ... 0xfe 0xff - * - * gst_byte_reader_masked_scan_uint32 (reader, 0xffffffff, 0x00010203, 0, 256); - * // -> returns 0 - * gst_byte_reader_masked_scan_uint32 (reader, 0xffffffff, 0x00010203, 1, 255); - * // -> returns -1 - * gst_byte_reader_masked_scan_uint32 (reader, 0xffffffff, 0x01020304, 1, 255); - * // -> returns 1 - * gst_byte_reader_masked_scan_uint32 (reader, 0xffff, 0x0001, 0, 256); - * // -> returns -1 - * gst_byte_reader_masked_scan_uint32 (reader, 0xffff, 0x0203, 0, 256); - * // -> returns 0 - * gst_byte_reader_masked_scan_uint32 (reader, 0xffff0000, 0x02030000, 0, 256); - * // -> returns 2 - * gst_byte_reader_masked_scan_uint32 (reader, 0xffff0000, 0x02030000, 0, 4); - * // -> returns -1 - * ]| - */ -guint -gst_byte_reader_masked_scan_uint32 (const GstByteReader * reader, guint32 mask, - guint32 pattern, guint offset, guint size) -{ - return _masked_scan_uint32_peek (reader, mask, pattern, offset, size, NULL); -} - -/** - * gst_byte_reader_masked_scan_uint32_peek: - * @reader: a #GstByteReader - * @mask: mask to apply to data before matching against @pattern - * @pattern: pattern to match (after mask is applied) - * @offset: offset from which to start scanning, relative to the current - * position - * @size: number of bytes to scan from offset - * @value: (out): pointer to uint32 to return matching data - * - * Scan for pattern @pattern with applied mask @mask in the byte reader data, - * starting from offset @offset relative to the current position. - * - * The bytes in @pattern and @mask are interpreted left-to-right, regardless - * of endianness. All four bytes of the pattern must be present in the - * byte reader data for it to match, even if the first or last bytes are masked - * out. - * - * It is an error to call this function without making sure that there is - * enough data (offset+size bytes) in the byte reader. - * - * Returns: offset of the first match, or -1 if no match was found. - * - * Since: 1.6 - */ -guint -gst_byte_reader_masked_scan_uint32_peek (const GstByteReader * reader, - guint32 mask, guint32 pattern, guint offset, guint size, guint32 * value) -{ - return _masked_scan_uint32_peek (reader, mask, pattern, offset, size, value); -} - -#define GST_BYTE_READER_SCAN_STRING(bits) \ -static guint \ -gst_byte_reader_scan_string_utf##bits (const GstByteReader * reader) \ -{ \ - guint len, off, max_len; \ - \ - max_len = (reader->size - reader->byte) / sizeof (guint##bits); \ - \ - /* need at least a single NUL terminator */ \ - if (max_len < 1) \ - return 0; \ - \ - len = 0; \ - off = reader->byte; \ - /* endianness does not matter if we are looking for a NUL terminator */ \ - while (GST_READ_UINT##bits##_LE (&reader->data[off]) != 0) { \ - ++len; \ - off += sizeof (guint##bits); \ - /* have we reached the end without finding a NUL terminator? */ \ - if (len == max_len) \ - return 0; \ - } \ - /* return size in bytes including the NUL terminator (hence the +1) */ \ - return (len + 1) * sizeof (guint##bits); \ -} - -#define GST_READ_UINT8_LE GST_READ_UINT8 -GST_BYTE_READER_SCAN_STRING (8); -#undef GST_READ_UINT8_LE -GST_BYTE_READER_SCAN_STRING (16); -GST_BYTE_READER_SCAN_STRING (32); - -#define GST_BYTE_READER_SKIP_STRING(bits) \ -gboolean \ -gst_byte_reader_skip_string_utf##bits (GstByteReader * reader) \ -{ \ - guint size; /* size in bytes including the terminator */ \ - \ - g_return_val_if_fail (reader != NULL, FALSE); \ - \ - size = gst_byte_reader_scan_string_utf##bits (reader); \ - reader->byte += size; \ - return (size > 0); \ -} - -/** - * gst_byte_reader_skip_string: - * @reader: a #GstByteReader instance - * - * Skips a NUL-terminated string in the #GstByteReader instance, advancing - * the current position to the byte after the string. This will work for - * any NUL-terminated string with a character width of 8 bits, so ASCII, - * UTF-8, ISO-8859-N etc. - * - * This function will fail if no NUL-terminator was found in in the data. - * - * Returns: %TRUE if a string could be skipped, %FALSE otherwise. - */ -/** - * gst_byte_reader_skip_string_utf8: - * @reader: a #GstByteReader instance - * - * Skips a NUL-terminated string in the #GstByteReader instance, advancing - * the current position to the byte after the string. This will work for - * any NUL-terminated string with a character width of 8 bits, so ASCII, - * UTF-8, ISO-8859-N etc. No input checking for valid UTF-8 is done. - * - * This function will fail if no NUL-terminator was found in in the data. - * - * Returns: %TRUE if a string could be skipped, %FALSE otherwise. - */ -GST_BYTE_READER_SKIP_STRING (8); - -/** - * gst_byte_reader_skip_string_utf16: - * @reader: a #GstByteReader instance - * - * Skips a NUL-terminated UTF-16 string in the #GstByteReader instance, - * advancing the current position to the byte after the string. - * - * No input checking for valid UTF-16 is done. - * - * This function will fail if no NUL-terminator was found in in the data. - * - * Returns: %TRUE if a string could be skipped, %FALSE otherwise. - */ -GST_BYTE_READER_SKIP_STRING (16); - -/** - * gst_byte_reader_skip_string_utf32: - * @reader: a #GstByteReader instance - * - * Skips a NUL-terminated UTF-32 string in the #GstByteReader instance, - * advancing the current position to the byte after the string. - * - * No input checking for valid UTF-32 is done. - * - * This function will fail if no NUL-terminator was found in in the data. - * - * Returns: %TRUE if a string could be skipped, %FALSE otherwise. - */ -GST_BYTE_READER_SKIP_STRING (32); - -/** - * gst_byte_reader_peek_string: - * @reader: a #GstByteReader instance - * @str: (out) (transfer none) (array zero-terminated=1): address of a - * #gchar pointer variable in which to store the result - * - * Returns a constant pointer to the current data position if there is - * a NUL-terminated string in the data (this could be just a NUL terminator). - * The current position will be maintained. This will work for any - * NUL-terminated string with a character width of 8 bits, so ASCII, - * UTF-8, ISO-8859-N etc. - * - * This function will fail if no NUL-terminator was found in in the data. - * - * Returns: %TRUE if a string could be skipped, %FALSE otherwise. - */ -/** - * gst_byte_reader_peek_string_utf8: - * @reader: a #GstByteReader instance - * @str: (out) (transfer none) (array zero-terminated=1): address of a - * #gchar pointer variable in which to store the result - * - * Returns a constant pointer to the current data position if there is - * a NUL-terminated string in the data (this could be just a NUL terminator). - * The current position will be maintained. This will work for any - * NUL-terminated string with a character width of 8 bits, so ASCII, - * UTF-8, ISO-8859-N etc. - * - * No input checking for valid UTF-8 is done. - * - * This function will fail if no NUL-terminator was found in in the data. - * - * Returns: %TRUE if a string could be skipped, %FALSE otherwise. - */ -gboolean -gst_byte_reader_peek_string_utf8 (const GstByteReader * reader, - const gchar ** str) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (str != NULL, FALSE); - - if (gst_byte_reader_scan_string_utf8 (reader) > 0) { - *str = (const gchar *) (reader->data + reader->byte); - } else { - *str = NULL; - } - return (*str != NULL); -} - -/** - * gst_byte_reader_get_string_utf8: - * @reader: a #GstByteReader instance - * @str: (out) (transfer none) (array zero-terminated=1): address of a - * #gchar pointer variable in which to store the result - * - * Returns a constant pointer to the current data position if there is - * a NUL-terminated string in the data (this could be just a NUL terminator), - * advancing the current position to the byte after the string. This will work - * for any NUL-terminated string with a character width of 8 bits, so ASCII, - * UTF-8, ISO-8859-N etc. - * - * No input checking for valid UTF-8 is done. - * - * This function will fail if no NUL-terminator was found in in the data. - * - * Returns: %TRUE if a string could be found, %FALSE otherwise. - */ -gboolean -gst_byte_reader_get_string_utf8 (GstByteReader * reader, const gchar ** str) -{ - guint size; /* size in bytes including the terminator */ - - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (str != NULL, FALSE); - - size = gst_byte_reader_scan_string_utf8 (reader); - if (size == 0) { - *str = NULL; - return FALSE; - } - - *str = (const gchar *) (reader->data + reader->byte); - reader->byte += size; - return TRUE; -} - -#define GST_BYTE_READER_DUP_STRING(bits,type) \ -gboolean \ -gst_byte_reader_dup_string_utf##bits (GstByteReader * reader, type ** str) \ -{ \ - guint size; /* size in bytes including the terminator */ \ - \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (str != NULL, FALSE); \ - \ - size = gst_byte_reader_scan_string_utf##bits (reader); \ - if (size == 0) { \ - *str = NULL; \ - return FALSE; \ - } \ - *str = g_memdup2 (reader->data + reader->byte, size); \ - reader->byte += size; \ - return TRUE; \ -} - -/** - * gst_byte_reader_dup_string_utf8: - * @reader: a #GstByteReader instance - * @str: (out) (transfer full) (array zero-terminated=1): address of a - * #gchar pointer variable in which to store the result - * - * Free-function: g_free - * - * FIXME:Reads (copies) a NUL-terminated string in the #GstByteReader instance, - * advancing the current position to the byte after the string. This will work - * for any NUL-terminated string with a character width of 8 bits, so ASCII, - * UTF-8, ISO-8859-N etc. No input checking for valid UTF-8 is done. - * - * This function will fail if no NUL-terminator was found in in the data. - * - * Returns: %TRUE if a string could be read into @str, %FALSE otherwise. The - * string put into @str must be freed with g_free() when no longer needed. - */ -GST_BYTE_READER_DUP_STRING (8, gchar); - -/** - * gst_byte_reader_dup_string_utf16: - * @reader: a #GstByteReader instance - * @str: (out) (transfer full) (array zero-terminated=1): address of a - * #guint16 pointer variable in which to store the result - * - * Free-function: g_free - * - * Returns a newly-allocated copy of the current data position if there is - * a NUL-terminated UTF-16 string in the data (this could be an empty string - * as well), and advances the current position. - * - * No input checking for valid UTF-16 is done. This function is endianness - * agnostic - you should not assume the UTF-16 characters are in host - * endianness. - * - * This function will fail if no NUL-terminator was found in in the data. - * - * Note: there is no peek or get variant of this function to ensure correct - * byte alignment of the UTF-16 string. - * - * Returns: %TRUE if a string could be read, %FALSE otherwise. The - * string put into @str must be freed with g_free() when no longer needed. - */ -GST_BYTE_READER_DUP_STRING (16, guint16); - -/** - * gst_byte_reader_dup_string_utf32: - * @reader: a #GstByteReader instance - * @str: (out) (transfer full) (array zero-terminated=1): address of a - * #guint32 pointer variable in which to store the result - * - * Free-function: g_free - * - * Returns a newly-allocated copy of the current data position if there is - * a NUL-terminated UTF-32 string in the data (this could be an empty string - * as well), and advances the current position. - * - * No input checking for valid UTF-32 is done. This function is endianness - * agnostic - you should not assume the UTF-32 characters are in host - * endianness. - * - * This function will fail if no NUL-terminator was found in in the data. - * - * Note: there is no peek or get variant of this function to ensure correct - * byte alignment of the UTF-32 string. - * - * Returns: %TRUE if a string could be read, %FALSE otherwise. The - * string put into @str must be freed with g_free() when no longer needed. - */ -GST_BYTE_READER_DUP_STRING (32, guint32); diff --git a/libs/gst/base/gstbytereader.h b/libs/gst/base/gstbytereader.h deleted file mode 100644 index 2130a8a008..0000000000 --- a/libs/gst/base/gstbytereader.h +++ /dev/null @@ -1,684 +0,0 @@ -/* GStreamer byte reader - * - * Copyright (C) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>. - * Copyright (C) 2009 Tim-Philipp Müller <tim centricular net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_BYTE_READER_H__ -#define __GST_BYTE_READER_H__ - -#include <gst/gst.h> -#include <gst/base/base-prelude.h> - -G_BEGIN_DECLS - -#define GST_BYTE_READER(reader) ((GstByteReader *) (reader)) - -/** - * GstByteReader: - * @data: (array length=size): Data from which the bit reader will - * read - * @size: Size of @data in bytes - * @byte: Current byte position - * - * A byte reader instance. - */ -typedef struct { - const guint8 *data; - guint size; - - guint byte; /* Byte position */ - - /* < private > */ - gpointer _gst_reserved[GST_PADDING]; -} GstByteReader; - -GST_BASE_API -GstByteReader * gst_byte_reader_new (const guint8 *data, guint size) G_GNUC_MALLOC; - -GST_BASE_API -void gst_byte_reader_free (GstByteReader *reader); - -GST_BASE_API -void gst_byte_reader_init (GstByteReader *reader, const guint8 *data, guint size); - -GST_BASE_API -gboolean gst_byte_reader_peek_sub_reader (GstByteReader * reader, - GstByteReader * sub_reader, - guint size); -GST_BASE_API -gboolean gst_byte_reader_get_sub_reader (GstByteReader * reader, - GstByteReader * sub_reader, - guint size); -GST_BASE_API -gboolean gst_byte_reader_set_pos (GstByteReader *reader, guint pos); - -GST_BASE_API -guint gst_byte_reader_get_pos (const GstByteReader *reader); - -GST_BASE_API -guint gst_byte_reader_get_remaining (const GstByteReader *reader); - -GST_BASE_API -guint gst_byte_reader_get_size (const GstByteReader *reader); - -GST_BASE_API -gboolean gst_byte_reader_skip (GstByteReader *reader, guint nbytes); - -GST_BASE_API -gboolean gst_byte_reader_get_uint8 (GstByteReader *reader, guint8 *val); - -GST_BASE_API -gboolean gst_byte_reader_get_int8 (GstByteReader *reader, gint8 *val); - -GST_BASE_API -gboolean gst_byte_reader_get_uint16_le (GstByteReader *reader, guint16 *val); - -GST_BASE_API -gboolean gst_byte_reader_get_int16_le (GstByteReader *reader, gint16 *val); - -GST_BASE_API -gboolean gst_byte_reader_get_uint16_be (GstByteReader *reader, guint16 *val); - -GST_BASE_API -gboolean gst_byte_reader_get_int16_be (GstByteReader *reader, gint16 *val); - -GST_BASE_API -gboolean gst_byte_reader_get_uint24_le (GstByteReader *reader, guint32 *val); - -GST_BASE_API -gboolean gst_byte_reader_get_int24_le (GstByteReader *reader, gint32 *val); - -GST_BASE_API -gboolean gst_byte_reader_get_uint24_be (GstByteReader *reader, guint32 *val); - -GST_BASE_API -gboolean gst_byte_reader_get_int24_be (GstByteReader *reader, gint32 *val); - -GST_BASE_API -gboolean gst_byte_reader_get_uint32_le (GstByteReader *reader, guint32 *val); - -GST_BASE_API -gboolean gst_byte_reader_get_int32_le (GstByteReader *reader, gint32 *val); - -GST_BASE_API -gboolean gst_byte_reader_get_uint32_be (GstByteReader *reader, guint32 *val); - -GST_BASE_API -gboolean gst_byte_reader_get_int32_be (GstByteReader *reader, gint32 *val); - -GST_BASE_API -gboolean gst_byte_reader_get_uint64_le (GstByteReader *reader, guint64 *val); - -GST_BASE_API -gboolean gst_byte_reader_get_int64_le (GstByteReader *reader, gint64 *val); - -GST_BASE_API -gboolean gst_byte_reader_get_uint64_be (GstByteReader *reader, guint64 *val); - -GST_BASE_API -gboolean gst_byte_reader_get_int64_be (GstByteReader *reader, gint64 *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_uint8 (const GstByteReader *reader, guint8 *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_int8 (const GstByteReader *reader, gint8 *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_uint16_le (const GstByteReader *reader, guint16 *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_int16_le (const GstByteReader *reader, gint16 *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_uint16_be (const GstByteReader *reader, guint16 *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_int16_be (const GstByteReader *reader, gint16 *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_uint24_le (const GstByteReader *reader, guint32 *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_int24_le (const GstByteReader *reader, gint32 *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_uint24_be (const GstByteReader *reader, guint32 *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_int24_be (const GstByteReader *reader, gint32 *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_uint32_le (const GstByteReader *reader, guint32 *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_int32_le (const GstByteReader *reader, gint32 *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_uint32_be (const GstByteReader *reader, guint32 *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_int32_be (const GstByteReader *reader, gint32 *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_uint64_le (const GstByteReader *reader, guint64 *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_int64_le (const GstByteReader *reader, gint64 *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_uint64_be (const GstByteReader *reader, guint64 *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_int64_be (const GstByteReader *reader, gint64 *val); - -GST_BASE_API -gboolean gst_byte_reader_get_float32_le (GstByteReader *reader, gfloat *val); - -GST_BASE_API -gboolean gst_byte_reader_get_float32_be (GstByteReader *reader, gfloat *val); - -GST_BASE_API -gboolean gst_byte_reader_get_float64_le (GstByteReader *reader, gdouble *val); - -GST_BASE_API -gboolean gst_byte_reader_get_float64_be (GstByteReader *reader, gdouble *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_float32_le (const GstByteReader *reader, gfloat *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_float32_be (const GstByteReader *reader, gfloat *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_float64_le (const GstByteReader *reader, gdouble *val); - -GST_BASE_API -gboolean gst_byte_reader_peek_float64_be (const GstByteReader *reader, gdouble *val); - -GST_BASE_API -gboolean gst_byte_reader_dup_data (GstByteReader * reader, guint size, guint8 ** val); - -GST_BASE_API -gboolean gst_byte_reader_get_data (GstByteReader * reader, guint size, const guint8 ** val); - -GST_BASE_API -gboolean gst_byte_reader_peek_data (const GstByteReader * reader, guint size, const guint8 ** val); - -#define gst_byte_reader_dup_string(reader,str) \ - gst_byte_reader_dup_string_utf8(reader,str) - -GST_BASE_API -gboolean gst_byte_reader_dup_string_utf8 (GstByteReader * reader, gchar ** str); - -GST_BASE_API -gboolean gst_byte_reader_dup_string_utf16 (GstByteReader * reader, guint16 ** str); - -GST_BASE_API -gboolean gst_byte_reader_dup_string_utf32 (GstByteReader * reader, guint32 ** str); - -#define gst_byte_reader_skip_string(reader) \ - gst_byte_reader_skip_string_utf8(reader) - -GST_BASE_API -gboolean gst_byte_reader_skip_string_utf8 (GstByteReader * reader); - -GST_BASE_API -gboolean gst_byte_reader_skip_string_utf16 (GstByteReader * reader); - -GST_BASE_API -gboolean gst_byte_reader_skip_string_utf32 (GstByteReader * reader); - -#define gst_byte_reader_get_string(reader,str) \ - gst_byte_reader_get_string_utf8(reader,str) - -#define gst_byte_reader_peek_string(reader,str) \ - gst_byte_reader_peek_string_utf8(reader,str) - -GST_BASE_API -gboolean gst_byte_reader_get_string_utf8 (GstByteReader * reader, const gchar ** str); - -GST_BASE_API -gboolean gst_byte_reader_peek_string_utf8 (const GstByteReader * reader, const gchar ** str); - -GST_BASE_API -guint gst_byte_reader_masked_scan_uint32 (const GstByteReader * reader, - guint32 mask, - guint32 pattern, - guint offset, - guint size); -GST_BASE_API -guint gst_byte_reader_masked_scan_uint32_peek (const GstByteReader * reader, - guint32 mask, - guint32 pattern, - guint offset, - guint size, - guint32 * value); - -/** - * GST_BYTE_READER_INIT: - * @data: Data from which the #GstByteReader should read - * @size: Size of @data in bytes - * - * A #GstByteReader must be initialized with this macro, before it can be - * used. This macro can used be to initialize a variable, but it cannot - * be assigned to a variable. In that case you have to use - * gst_byte_reader_init(). - */ -#define GST_BYTE_READER_INIT(data, size) {data, size, 0} - -/* unchecked variants */ -static inline void -gst_byte_reader_skip_unchecked (GstByteReader * reader, guint nbytes) -{ - reader->byte += nbytes; -} - -#define __GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(bits,type,lower,upper,adj) \ -\ -static inline type \ -gst_byte_reader_peek_##lower##_unchecked (const GstByteReader * reader) \ -{ \ - type val = (type) GST_READ_##upper (reader->data + reader->byte); \ - adj \ - return val; \ -} \ -\ -static inline type \ -gst_byte_reader_get_##lower##_unchecked (GstByteReader * reader) \ -{ \ - type val = gst_byte_reader_peek_##lower##_unchecked (reader); \ - reader->byte += bits / 8; \ - return val; \ -} - -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(8,guint8,uint8,UINT8,/* */) -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(8,gint8,int8,UINT8,/* */) - -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(16,guint16,uint16_le,UINT16_LE,/* */) -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(16,guint16,uint16_be,UINT16_BE,/* */) -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(16,gint16,int16_le,UINT16_LE,/* */) -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(16,gint16,int16_be,UINT16_BE,/* */) - -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(32,guint32,uint32_le,UINT32_LE,/* */) -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(32,guint32,uint32_be,UINT32_BE,/* */) -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(32,gint32,int32_le,UINT32_LE,/* */) -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(32,gint32,int32_be,UINT32_BE,/* */) - -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(24,guint32,uint24_le,UINT24_LE,/* */) -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(24,guint32,uint24_be,UINT24_BE,/* */) - -/* fix up the sign for 24-bit signed ints stored in 32-bit signed ints */ -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(24,gint32,int24_le,UINT24_LE, - if (val & 0x00800000) val |= 0xff000000;) -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(24,gint32,int24_be,UINT24_BE, - if (val & 0x00800000) val |= 0xff000000;) - -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(64,guint64,uint64_le,UINT64_LE,/* */) -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(64,guint64,uint64_be,UINT64_BE,/* */) -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(64,gint64,int64_le,UINT64_LE,/* */) -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(64,gint64,int64_be,UINT64_BE,/* */) - -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(32,gfloat,float32_le,FLOAT_LE,/* */) -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(32,gfloat,float32_be,FLOAT_BE,/* */) -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(64,gdouble,float64_le,DOUBLE_LE,/* */) -__GST_BYTE_READER_GET_PEEK_BITS_UNCHECKED(64,gdouble,float64_be,DOUBLE_BE,/* */) - -#undef __GET_PEEK_BITS_UNCHECKED - -static inline const guint8 * -gst_byte_reader_peek_data_unchecked (const GstByteReader * reader) -{ - return (const guint8 *) (reader->data + reader->byte); -} - -static inline const guint8 * -gst_byte_reader_get_data_unchecked (GstByteReader * reader, guint size) -{ - const guint8 *data; - - data = gst_byte_reader_peek_data_unchecked (reader); - gst_byte_reader_skip_unchecked (reader, size); - return data; -} - -static inline guint8 * -gst_byte_reader_dup_data_unchecked (GstByteReader * reader, guint size) -{ - gconstpointer data = gst_byte_reader_get_data_unchecked (reader, size); - guint8 *dup_data = (guint8 *) g_malloc (size); - - memcpy (dup_data, data, size); - return dup_data; -} - -/* Unchecked variants that should not be used */ -static inline guint -_gst_byte_reader_get_pos_unchecked (const GstByteReader * reader) -{ - return reader->byte; -} - -static inline guint -_gst_byte_reader_get_remaining_unchecked (const GstByteReader * reader) -{ - return reader->size - reader->byte; -} - -static inline guint -_gst_byte_reader_get_size_unchecked (const GstByteReader * reader) -{ - return reader->size; -} - -/* inlined variants (do not use directly) */ - -static inline guint -_gst_byte_reader_get_remaining_inline (const GstByteReader * reader) -{ - g_return_val_if_fail (reader != NULL, 0); - - return _gst_byte_reader_get_remaining_unchecked (reader); -} - -static inline guint -_gst_byte_reader_get_size_inline (const GstByteReader * reader) -{ - g_return_val_if_fail (reader != NULL, 0); - - return _gst_byte_reader_get_size_unchecked (reader); -} - -#define __GST_BYTE_READER_GET_PEEK_BITS_INLINE(bits,type,name) \ -\ -static inline gboolean \ -_gst_byte_reader_peek_##name##_inline (const GstByteReader * reader, type * val) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (_gst_byte_reader_get_remaining_unchecked (reader) < (bits / 8)) \ - return FALSE; \ -\ - *val = gst_byte_reader_peek_##name##_unchecked (reader); \ - return TRUE; \ -} \ -\ -static inline gboolean \ -_gst_byte_reader_get_##name##_inline (GstByteReader * reader, type * val) \ -{ \ - g_return_val_if_fail (reader != NULL, FALSE); \ - g_return_val_if_fail (val != NULL, FALSE); \ - \ - if (_gst_byte_reader_get_remaining_unchecked (reader) < (bits / 8)) \ - return FALSE; \ -\ - *val = gst_byte_reader_get_##name##_unchecked (reader); \ - return TRUE; \ -} - -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(8,guint8,uint8) -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(8,gint8,int8) - -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(16,guint16,uint16_le) -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(16,guint16,uint16_be) -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(16,gint16,int16_le) -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(16,gint16,int16_be) - -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(32,guint32,uint32_le) -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(32,guint32,uint32_be) -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(32,gint32,int32_le) -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(32,gint32,int32_be) - -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(24,guint32,uint24_le) -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(24,guint32,uint24_be) -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(24,gint32,int24_le) -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(24,gint32,int24_be) - -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(64,guint64,uint64_le) -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(64,guint64,uint64_be) -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(64,gint64,int64_le) -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(64,gint64,int64_be) - -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(32,gfloat,float32_le) -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(32,gfloat,float32_be) -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(64,gdouble,float64_le) -__GST_BYTE_READER_GET_PEEK_BITS_INLINE(64,gdouble,float64_be) - -#undef __GST_BYTE_READER_GET_PEEK_BITS_INLINE - -#ifndef GST_BYTE_READER_DISABLE_INLINES - -#define gst_byte_reader_init(reader,data,size) \ - _gst_byte_reader_init_inline(reader,data,size) - -#define gst_byte_reader_get_remaining(reader) \ - _gst_byte_reader_get_remaining_inline(reader) - -#define gst_byte_reader_get_size(reader) \ - _gst_byte_reader_get_size_inline(reader) - -#define gst_byte_reader_get_pos(reader) \ - _gst_byte_reader_get_pos_inline(reader) - -/* we use defines here so we can add the G_LIKELY() */ -#define gst_byte_reader_get_uint8(reader,val) \ - G_LIKELY(_gst_byte_reader_get_uint8_inline(reader,val)) -#define gst_byte_reader_get_int8(reader,val) \ - G_LIKELY(_gst_byte_reader_get_int8_inline(reader,val)) -#define gst_byte_reader_get_uint16_le(reader,val) \ - G_LIKELY(_gst_byte_reader_get_uint16_le_inline(reader,val)) -#define gst_byte_reader_get_int16_le(reader,val) \ - G_LIKELY(_gst_byte_reader_get_int16_le_inline(reader,val)) -#define gst_byte_reader_get_uint16_be(reader,val) \ - G_LIKELY(_gst_byte_reader_get_uint16_be_inline(reader,val)) -#define gst_byte_reader_get_int16_be(reader,val) \ - G_LIKELY(_gst_byte_reader_get_int16_be_inline(reader,val)) -#define gst_byte_reader_get_uint24_le(reader,val) \ - G_LIKELY(_gst_byte_reader_get_uint24_le_inline(reader,val)) -#define gst_byte_reader_get_int24_le(reader,val) \ - G_LIKELY(_gst_byte_reader_get_int24_le_inline(reader,val)) -#define gst_byte_reader_get_uint24_be(reader,val) \ - G_LIKELY(_gst_byte_reader_get_uint24_be_inline(reader,val)) -#define gst_byte_reader_get_int24_be(reader,val) \ - G_LIKELY(_gst_byte_reader_get_int24_be_inline(reader,val)) -#define gst_byte_reader_get_uint32_le(reader,val) \ - G_LIKELY(_gst_byte_reader_get_uint32_le_inline(reader,val)) -#define gst_byte_reader_get_int32_le(reader,val) \ - G_LIKELY(_gst_byte_reader_get_int32_le_inline(reader,val)) -#define gst_byte_reader_get_uint32_be(reader,val) \ - G_LIKELY(_gst_byte_reader_get_uint32_be_inline(reader,val)) -#define gst_byte_reader_get_int32_be(reader,val) \ - G_LIKELY(_gst_byte_reader_get_int32_be_inline(reader,val)) -#define gst_byte_reader_get_uint64_le(reader,val) \ - G_LIKELY(_gst_byte_reader_get_uint64_le_inline(reader,val)) -#define gst_byte_reader_get_int64_le(reader,val) \ - G_LIKELY(_gst_byte_reader_get_int64_le_inline(reader,val)) -#define gst_byte_reader_get_uint64_be(reader,val) \ - G_LIKELY(_gst_byte_reader_get_uint64_be_inline(reader,val)) -#define gst_byte_reader_get_int64_be(reader,val) \ - G_LIKELY(_gst_byte_reader_get_int64_be_inline(reader,val)) - -#define gst_byte_reader_peek_uint8(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_uint8_inline(reader,val)) -#define gst_byte_reader_peek_int8(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_int8_inline(reader,val)) -#define gst_byte_reader_peek_uint16_le(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_uint16_le_inline(reader,val)) -#define gst_byte_reader_peek_int16_le(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_int16_le_inline(reader,val)) -#define gst_byte_reader_peek_uint16_be(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_uint16_be_inline(reader,val)) -#define gst_byte_reader_peek_int16_be(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_int16_be_inline(reader,val)) -#define gst_byte_reader_peek_uint24_le(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_uint24_le_inline(reader,val)) -#define gst_byte_reader_peek_int24_le(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_int24_le_inline(reader,val)) -#define gst_byte_reader_peek_uint24_be(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_uint24_be_inline(reader,val)) -#define gst_byte_reader_peek_int24_be(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_int24_be_inline(reader,val)) -#define gst_byte_reader_peek_uint32_le(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_uint32_le_inline(reader,val)) -#define gst_byte_reader_peek_int32_le(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_int32_le_inline(reader,val)) -#define gst_byte_reader_peek_uint32_be(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_uint32_be_inline(reader,val)) -#define gst_byte_reader_peek_int32_be(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_int32_be_inline(reader,val)) -#define gst_byte_reader_peek_uint64_le(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_uint64_le_inline(reader,val)) -#define gst_byte_reader_peek_int64_le(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_int64_le_inline(reader,val)) -#define gst_byte_reader_peek_uint64_be(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_uint64_be_inline(reader,val)) -#define gst_byte_reader_peek_int64_be(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_int64_be_inline(reader,val)) - -#define gst_byte_reader_get_float32_le(reader,val) \ - G_LIKELY(_gst_byte_reader_get_float32_le_inline(reader,val)) -#define gst_byte_reader_get_float32_be(reader,val) \ - G_LIKELY(_gst_byte_reader_get_float32_be_inline(reader,val)) -#define gst_byte_reader_get_float64_le(reader,val) \ - G_LIKELY(_gst_byte_reader_get_float64_le_inline(reader,val)) -#define gst_byte_reader_get_float64_be(reader,val) \ - G_LIKELY(_gst_byte_reader_get_float64_be_inline(reader,val)) -#define gst_byte_reader_peek_float32_le(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_float32_le_inline(reader,val)) -#define gst_byte_reader_peek_float32_be(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_float32_be_inline(reader,val)) -#define gst_byte_reader_peek_float64_le(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_float64_le_inline(reader,val)) -#define gst_byte_reader_peek_float64_be(reader,val) \ - G_LIKELY(_gst_byte_reader_peek_float64_be_inline(reader,val)) - -#endif /* GST_BYTE_READER_DISABLE_INLINES */ - -static inline void -_gst_byte_reader_init_inline (GstByteReader * reader, const guint8 * data, guint size) -{ - g_return_if_fail (reader != NULL); - - reader->data = data; - reader->size = size; - reader->byte = 0; -} - -static inline gboolean -_gst_byte_reader_peek_sub_reader_inline (GstByteReader * reader, - GstByteReader * sub_reader, guint size) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (sub_reader != NULL, FALSE); - - if (_gst_byte_reader_get_remaining_unchecked (reader) < size) - return FALSE; - - sub_reader->data = reader->data + reader->byte; - sub_reader->byte = 0; - sub_reader->size = size; - return TRUE; -} - -static inline gboolean -_gst_byte_reader_get_sub_reader_inline (GstByteReader * reader, - GstByteReader * sub_reader, guint size) -{ - if (!_gst_byte_reader_peek_sub_reader_inline (reader, sub_reader, size)) - return FALSE; - gst_byte_reader_skip_unchecked (reader, size); - return TRUE; -} - -static inline gboolean -_gst_byte_reader_dup_data_inline (GstByteReader * reader, guint size, guint8 ** val) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (G_UNLIKELY (size > reader->size || _gst_byte_reader_get_remaining_unchecked (reader) < size)) - return FALSE; - - *val = gst_byte_reader_dup_data_unchecked (reader, size); - return TRUE; -} - -static inline gboolean -_gst_byte_reader_get_data_inline (GstByteReader * reader, guint size, const guint8 ** val) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (G_UNLIKELY (size > reader->size || _gst_byte_reader_get_remaining_unchecked (reader) < size)) - return FALSE; - - *val = gst_byte_reader_get_data_unchecked (reader, size); - return TRUE; -} - -static inline gboolean -_gst_byte_reader_peek_data_inline (const GstByteReader * reader, guint size, const guint8 ** val) -{ - g_return_val_if_fail (reader != NULL, FALSE); - g_return_val_if_fail (val != NULL, FALSE); - - if (G_UNLIKELY (size > reader->size || _gst_byte_reader_get_remaining_unchecked (reader) < size)) - return FALSE; - - *val = gst_byte_reader_peek_data_unchecked (reader); - return TRUE; -} - -static inline guint -_gst_byte_reader_get_pos_inline (const GstByteReader * reader) -{ - g_return_val_if_fail (reader != NULL, 0); - - return _gst_byte_reader_get_pos_unchecked (reader); -} - -static inline gboolean -_gst_byte_reader_skip_inline (GstByteReader * reader, guint nbytes) -{ - g_return_val_if_fail (reader != NULL, FALSE); - - if (G_UNLIKELY (_gst_byte_reader_get_remaining_unchecked (reader) < nbytes)) - return FALSE; - - reader->byte += nbytes; - return TRUE; -} - -#ifndef GST_BYTE_READER_DISABLE_INLINES - -#define gst_byte_reader_dup_data(reader,size,val) \ - G_LIKELY(_gst_byte_reader_dup_data_inline(reader,size,val)) -#define gst_byte_reader_get_data(reader,size,val) \ - G_LIKELY(_gst_byte_reader_get_data_inline(reader,size,val)) -#define gst_byte_reader_peek_data(reader,size,val) \ - G_LIKELY(_gst_byte_reader_peek_data_inline(reader,size,val)) -#define gst_byte_reader_skip(reader,nbytes) \ - G_LIKELY(_gst_byte_reader_skip_inline(reader,nbytes)) - -#endif /* GST_BYTE_READER_DISABLE_INLINES */ - -G_END_DECLS - -#endif /* __GST_BYTE_READER_H__ */ diff --git a/libs/gst/base/gstbytewriter-docs.h b/libs/gst/base/gstbytewriter-docs.h deleted file mode 100644 index 3cc5962d41..0000000000 --- a/libs/gst/base/gstbytewriter-docs.h +++ /dev/null @@ -1,269 +0,0 @@ -/* GStreamer byte writer dummy header for gtk-doc - * Copyright (C) 2010 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., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/* This header is not installed, it just contains stuff for gtk-doc to parse, - * in particular docs and some dummy function declarations for the static - * inline functions we generate via macros in gstbitreader.h. - */ - -#error "This header should never be included in code, it is only for gtk-doc" - -/** - * gst_byte_writer_put_uint8_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a unsigned 8 bit integer to @writer without checking if there - * is enough free space available in the byte writer. - */ -void gst_byte_writer_put_uint8_unchecked (GstByteWriter *writer, guint8 val); - -/** - * gst_byte_writer_put_uint16_be_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a unsigned big endian 16 bit integer to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_uint16_be_unchecked (GstByteWriter *writer, guint16 val); - -/** - * gst_byte_writer_put_uint24_be_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a unsigned big endian 24 bit integer to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_uint24_be_unchecked (GstByteWriter *writer, guint32 val); - -/** - * gst_byte_writer_put_uint32_be_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a unsigned big endian 32 bit integer to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_uint32_be_unchecked (GstByteWriter *writer, guint32 val); - -/** - * gst_byte_writer_put_uint64_be_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a unsigned big endian 64 bit integer to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_uint64_be_unchecked (GstByteWriter *writer, guint64 val); - -/** - * gst_byte_writer_put_uint16_le_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a unsigned little endian 16 bit integer to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_uint16_le_unchecked (GstByteWriter *writer, guint16 val); - -/** - * gst_byte_writer_put_uint24_le_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a unsigned little endian 24 bit integer to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_uint24_le_unchecked (GstByteWriter *writer, guint32 val); - -/** - * gst_byte_writer_put_uint32_le_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a unsigned little endian 32 bit integer to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_uint32_le_unchecked (GstByteWriter *writer, guint32 val); - -/** - * gst_byte_writer_put_uint64_le_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a unsigned little endian 64 bit integer to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_uint64_le_unchecked (GstByteWriter *writer, guint64 val); - -/** - * gst_byte_writer_put_int8: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a signed 8 bit integer to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_int8_unchecked (GstByteWriter *writer, gint8 val); - -/** - * gst_byte_writer_put_int16_be_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a signed big endian 16 bit integer to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_int16_be_unchecked (GstByteWriter *writer, gint16 val); - -/** - * gst_byte_writer_put_int24_be_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a signed big endian 24 bit integer to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_int24_be_unchecked (GstByteWriter *writer, gint32 val); - -/** - * gst_byte_writer_put_int32_be_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a signed big endian 32 bit integer to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_int32_be_unchecked (GstByteWriter *writer, gint32 val); - -/** - * gst_byte_writer_put_int64_be_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a signed big endian 64 bit integer to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_int64_be_unchecked (GstByteWriter *writer, gint64 val); - -/** - * gst_byte_writer_put_int16_le_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a signed little endian 16 bit integer to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_int16_le_unchecked (GstByteWriter *writer, gint16 val); - -/** - * gst_byte_writer_put_int24_le_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a signed little endian 24 bit integer to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_int24_le_unchecked (GstByteWriter *writer, gint32 val); - -/** - * gst_byte_writer_put_int32_le_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a signed little endian 32 bit integer to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_int32_le_unchecked (GstByteWriter *writer, gint32 val); - -/** - * gst_byte_writer_put_int64_le_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a signed little endian 64 bit integer to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_int64_le_unchecked (GstByteWriter *writer, gint64 val); - -/** - * gst_byte_writer_put_float32_be_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a big endian 32 bit float to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_float32_be_unchecked (GstByteWriter *writer, gfloat val); - -/** - * gst_byte_writer_put_float64_be_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a big endian 64 bit float to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_float64_be_unchecked (GstByteWriter *writer, gdouble val); - -/** - * gst_byte_writer_put_float32_le_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a little endian 32 bit float to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_float32_le_unchecked (GstByteWriter *writer, gfloat val); - -/** - * gst_byte_writer_put_float64_le_unchecked: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a little endian 64 bit float to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_float64_le_unchecked (GstByteWriter *writer, gdouble val); - -/** - * gst_byte_writer_put_data_unchecked: - * @writer: #GstByteWriter instance - * @data: (in) (transfer none) (array length=size): Data to write - * @size: Size of @data in bytes - * - * Writes @size bytes of @data to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_put_data_unchecked (GstByteWriter *writer, const guint8 *data, guint size); - -/** - * gst_byte_writer_fill_unchecked: - * @writer: #GstByteWriter instance - * @value: Value to be written - * @size: Number of bytes to be written - - * - * Writes @size bytes containing @value to @writer without - * checking if there is enough free space available in the byte writer. - */ -void gst_byte_writer_fill_unchecked (GstByteWriter *writer, guint8 value, guint size); - diff --git a/libs/gst/base/gstbytewriter.c b/libs/gst/base/gstbytewriter.c deleted file mode 100644 index aec10932e4..0000000000 --- a/libs/gst/base/gstbytewriter.c +++ /dev/null @@ -1,710 +0,0 @@ -/* GStreamer byte writer - * - * 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., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#define GST_BYTE_WRITER_DISABLE_INLINES -#include "gstbytewriter.h" - -#include "gst/glib-compat-private.h" - -/** - * SECTION:gstbytewriter - * @title: GstByteWriter - * @short_description: Writes different integer, string and floating point - * types to a memory buffer and allows reading - * @symbols: - * - gst_byte_writer_put_uint8_unchecked - * - gst_byte_writer_put_uint16_be_unchecked - * - gst_byte_writer_put_uint24_be_unchecked - * - gst_byte_writer_put_uint32_be_unchecked - * - gst_byte_writer_put_uint64_be_unchecked - * - gst_byte_writer_put_uint16_le_unchecked - * - gst_byte_writer_put_uint24_le_unchecked - * - gst_byte_writer_put_uint32_le_unchecked - * - gst_byte_writer_put_uint64_le_unchecked - * - gst_byte_writer_put_int8_unchecked - * - gst_byte_writer_put_int16_be_unchecked - * - gst_byte_writer_put_int24_be_unchecked - * - gst_byte_writer_put_int32_be_unchecked - * - gst_byte_writer_put_int64_be_unchecked - * - gst_byte_writer_put_int16_le_unchecked - * - gst_byte_writer_put_int24_le_unchecked - * - gst_byte_writer_put_int32_le_unchecked - * - gst_byte_writer_put_int64_le_unchecked - * - gst_byte_writer_put_float32_be_unchecked - * - gst_byte_writer_put_float64_be_unchecked - * - gst_byte_writer_put_float32_le_unchecked - * - gst_byte_writer_put_float64_le_unchecked - * - gst_byte_writer_put_data_unchecked - * - gst_byte_writer_fill_unchecked - * - * #GstByteWriter provides a byte writer and reader that can write/read different - * integer and floating point types to/from a memory buffer. It provides functions - * for writing/reading signed/unsigned, little/big endian integers of 8, 16, 24, - * 32 and 64 bits and functions for reading little/big endian floating points numbers of - * 32 and 64 bits. It also provides functions to write/read NUL-terminated strings - * in various character encodings. - */ - -/** - * gst_byte_writer_new: (skip) - * - * Creates a new, empty #GstByteWriter instance - * - * Free-function: gst_byte_writer_free - * - * Returns: (transfer full): a new, empty #GstByteWriter instance - */ -GstByteWriter * -gst_byte_writer_new (void) -{ - GstByteWriter *ret = g_slice_new0 (GstByteWriter); - - ret->owned = TRUE; - return ret; -} - -/** - * gst_byte_writer_new_with_size: (skip) - * @size: Initial size of data - * @fixed: If %TRUE the data can't be reallocated - * - * Creates a new #GstByteWriter instance with the given - * initial data size. - * - * Free-function: gst_byte_writer_free - * - * Returns: (transfer full): a new #GstByteWriter instance - */ -GstByteWriter * -gst_byte_writer_new_with_size (guint size, gboolean fixed) -{ - GstByteWriter *ret = gst_byte_writer_new (); - - ret->alloc_size = size; - ret->parent.data = g_malloc (ret->alloc_size); - ret->fixed = fixed; - ret->owned = TRUE; - - return ret; -} - -/** - * gst_byte_writer_new_with_data: (skip) - * @data: Memory area for writing - * @size: Size of @data in bytes - * @initialized: If %TRUE the complete data can be read from the beginning - * - * Creates a new #GstByteWriter instance with the given - * memory area. If @initialized is %TRUE it is possible to - * read @size bytes from the #GstByteWriter from the beginning. - * - * Free-function: gst_byte_writer_free - * - * Returns: (transfer full): a new #GstByteWriter instance - */ -GstByteWriter * -gst_byte_writer_new_with_data (guint8 * data, guint size, gboolean initialized) -{ - GstByteWriter *ret = gst_byte_writer_new (); - - ret->parent.data = data; - ret->parent.size = (initialized) ? size : 0; - ret->alloc_size = size; - ret->fixed = TRUE; - ret->owned = FALSE; - - return ret; -} - -/** - * gst_byte_writer_init: - * @writer: #GstByteWriter instance - * - * Initializes @writer to an empty instance - */ -void -gst_byte_writer_init (GstByteWriter * writer) -{ - g_return_if_fail (writer != NULL); - - memset (writer, 0, sizeof (GstByteWriter)); - - writer->owned = TRUE; -} - -/** - * gst_byte_writer_init_with_size: - * @writer: #GstByteWriter instance - * @size: Initial size of data - * @fixed: If %TRUE the data can't be reallocated - * - * Initializes @writer with the given initial data size. - */ -void -gst_byte_writer_init_with_size (GstByteWriter * writer, guint size, - gboolean fixed) -{ - g_return_if_fail (writer != NULL); - - gst_byte_writer_init (writer); - - writer->parent.data = g_malloc (size); - writer->alloc_size = size; - writer->fixed = fixed; - writer->owned = TRUE; -} - -/** - * gst_byte_writer_init_with_data: - * @writer: #GstByteWriter instance - * @data: (array length=size) (transfer none): Memory area for writing - * @size: Size of @data in bytes - * @initialized: If %TRUE the complete data can be read from the beginning - * - * Initializes @writer with the given - * memory area. If @initialized is %TRUE it is possible to - * read @size bytes from the #GstByteWriter from the beginning. - */ -void -gst_byte_writer_init_with_data (GstByteWriter * writer, guint8 * data, - guint size, gboolean initialized) -{ - g_return_if_fail (writer != NULL); - - gst_byte_writer_init (writer); - - writer->parent.data = data; - writer->parent.size = (initialized) ? size : 0; - writer->alloc_size = size; - writer->fixed = TRUE; - writer->owned = FALSE; -} - -/** - * gst_byte_writer_reset: - * @writer: #GstByteWriter instance - * - * Resets @writer and frees the data if it's - * owned by @writer. - */ -void -gst_byte_writer_reset (GstByteWriter * writer) -{ - g_return_if_fail (writer != NULL); - - if (writer->owned) - g_free ((guint8 *) writer->parent.data); - memset (writer, 0, sizeof (GstByteWriter)); -} - -/** - * gst_byte_writer_reset_and_get_data: - * @writer: #GstByteWriter instance - * - * Resets @writer and returns the current data. - * - * Free-function: g_free - * - * Returns: (array) (transfer full): the current data. g_free() after - * usage. - */ -guint8 * -gst_byte_writer_reset_and_get_data (GstByteWriter * writer) -{ - guint8 *data; - - g_return_val_if_fail (writer != NULL, NULL); - - data = (guint8 *) writer->parent.data; - if (!writer->owned) - data = g_memdup2 (data, writer->parent.size); - writer->parent.data = NULL; - gst_byte_writer_reset (writer); - - return data; -} - -/** - * gst_byte_writer_reset_and_get_buffer: - * @writer: #GstByteWriter instance - * - * Resets @writer and returns the current data as buffer. - * - * Free-function: gst_buffer_unref - * - * Returns: (transfer full): the current data as buffer. gst_buffer_unref() - * after usage. - */ -GstBuffer * -gst_byte_writer_reset_and_get_buffer (GstByteWriter * writer) -{ - GstBuffer *buffer; - gpointer data; - gsize size; - - g_return_val_if_fail (writer != NULL, NULL); - - size = writer->parent.size; - data = gst_byte_writer_reset_and_get_data (writer); - - buffer = gst_buffer_new (); - if (data != NULL) { - gst_buffer_append_memory (buffer, - gst_memory_new_wrapped (0, data, size, 0, size, data, g_free)); - } - - return buffer; -} - -/** - * gst_byte_writer_free: - * @writer: (in) (transfer full): #GstByteWriter instance - * - * Frees @writer and all memory allocated by it. - */ -void -gst_byte_writer_free (GstByteWriter * writer) -{ - g_return_if_fail (writer != NULL); - - gst_byte_writer_reset (writer); - g_slice_free (GstByteWriter, writer); -} - -/** - * gst_byte_writer_free_and_get_data: - * @writer: (in) (transfer full): #GstByteWriter instance - * - * Frees @writer and all memory allocated by it except - * the current data, which is returned. - * - * Free-function: g_free - * - * Returns: (transfer full): the current data. g_free() after usage. - */ -guint8 * -gst_byte_writer_free_and_get_data (GstByteWriter * writer) -{ - guint8 *data; - - g_return_val_if_fail (writer != NULL, NULL); - - data = gst_byte_writer_reset_and_get_data (writer); - g_slice_free (GstByteWriter, writer); - - return data; -} - -/** - * gst_byte_writer_free_and_get_buffer: - * @writer: (in) (transfer full): #GstByteWriter instance - * - * Frees @writer and all memory allocated by it except - * the current data, which is returned as #GstBuffer. - * - * Free-function: gst_buffer_unref - * - * Returns: (transfer full): the current data as buffer. gst_buffer_unref() - * after usage. - */ -GstBuffer * -gst_byte_writer_free_and_get_buffer (GstByteWriter * writer) -{ - GstBuffer *buffer; - - g_return_val_if_fail (writer != NULL, NULL); - - buffer = gst_byte_writer_reset_and_get_buffer (writer); - g_slice_free (GstByteWriter, writer); - - return buffer; -} - -/** - * gst_byte_writer_get_remaining: - * @writer: #GstByteWriter instance - * - * Returns the remaining size of data that can still be written. If - * -1 is returned the remaining size is only limited by system resources. - * - * Returns: the remaining size of data that can still be written - */ -guint -gst_byte_writer_get_remaining (const GstByteWriter * writer) -{ - g_return_val_if_fail (writer != NULL, -1); - - if (!writer->fixed) - return -1; - else - return writer->alloc_size - writer->parent.byte; -} - -/** - * gst_byte_writer_ensure_free_space: - * @writer: #GstByteWriter instance - * @size: Number of bytes that should be available - * - * Checks if enough free space from the current write cursor is - * available and reallocates if necessary. - * - * Returns: %TRUE if at least @size bytes are still available - */ -gboolean -gst_byte_writer_ensure_free_space (GstByteWriter * writer, guint size) -{ - return _gst_byte_writer_ensure_free_space_inline (writer, size); -} - - -#define CREATE_WRITE_FUNC(bits,type,name,write_func) \ -gboolean \ -gst_byte_writer_put_##name (GstByteWriter *writer, type val) \ -{ \ - return _gst_byte_writer_put_##name##_inline (writer, val); \ -} - -CREATE_WRITE_FUNC (8, guint8, uint8, GST_WRITE_UINT8); -CREATE_WRITE_FUNC (8, gint8, int8, GST_WRITE_UINT8); -CREATE_WRITE_FUNC (16, guint16, uint16_le, GST_WRITE_UINT16_LE); -CREATE_WRITE_FUNC (16, guint16, uint16_be, GST_WRITE_UINT16_BE); -CREATE_WRITE_FUNC (16, gint16, int16_le, GST_WRITE_UINT16_LE); -CREATE_WRITE_FUNC (16, gint16, int16_be, GST_WRITE_UINT16_BE); -CREATE_WRITE_FUNC (24, guint32, uint24_le, GST_WRITE_UINT24_LE); -CREATE_WRITE_FUNC (24, guint32, uint24_be, GST_WRITE_UINT24_BE); -CREATE_WRITE_FUNC (24, gint32, int24_le, GST_WRITE_UINT24_LE); -CREATE_WRITE_FUNC (24, gint32, int24_be, GST_WRITE_UINT24_BE); -CREATE_WRITE_FUNC (32, guint32, uint32_le, GST_WRITE_UINT32_LE); -CREATE_WRITE_FUNC (32, guint32, uint32_be, GST_WRITE_UINT32_BE); -CREATE_WRITE_FUNC (32, gint32, int32_le, GST_WRITE_UINT32_LE); -CREATE_WRITE_FUNC (32, gint32, int32_be, GST_WRITE_UINT32_BE); -CREATE_WRITE_FUNC (64, guint64, uint64_le, GST_WRITE_UINT64_LE); -CREATE_WRITE_FUNC (64, guint64, uint64_be, GST_WRITE_UINT64_BE); -CREATE_WRITE_FUNC (64, gint64, int64_le, GST_WRITE_UINT64_LE); -CREATE_WRITE_FUNC (64, gint64, int64_be, GST_WRITE_UINT64_BE); - -CREATE_WRITE_FUNC (32, gfloat, float32_be, GST_WRITE_FLOAT_BE); -CREATE_WRITE_FUNC (32, gfloat, float32_le, GST_WRITE_FLOAT_LE); -CREATE_WRITE_FUNC (64, gdouble, float64_be, GST_WRITE_DOUBLE_BE); -CREATE_WRITE_FUNC (64, gdouble, float64_le, GST_WRITE_DOUBLE_LE); - -gboolean -gst_byte_writer_put_data (GstByteWriter * writer, const guint8 * data, - guint size) -{ - return _gst_byte_writer_put_data_inline (writer, data, size); -} - -gboolean -gst_byte_writer_fill (GstByteWriter * writer, guint8 value, guint size) -{ - return _gst_byte_writer_fill_inline (writer, value, size); -} - -#define CREATE_WRITE_STRING_FUNC(bits,type) \ -gboolean \ -gst_byte_writer_put_string_utf##bits (GstByteWriter *writer, const type * data) \ -{ \ - guint size = 0; \ - \ - g_return_val_if_fail (writer != NULL, FALSE); \ - \ - /* endianness does not matter if we are looking for a NUL terminator */ \ - while (data[size] != 0) { \ - /* have prevent overflow */ \ - if (G_UNLIKELY (size == G_MAXUINT)) \ - return FALSE; \ - ++size; \ - } \ - ++size; \ - \ - if (G_UNLIKELY (!_gst_byte_writer_ensure_free_space_inline(writer, size * (bits / 8)))) \ - return FALSE; \ - \ - _gst_byte_writer_put_data_inline (writer, (const guint8 *) data, size * (bits / 8)); \ - \ - return TRUE; \ -} - -CREATE_WRITE_STRING_FUNC (8, gchar); -CREATE_WRITE_STRING_FUNC (16, guint16); -CREATE_WRITE_STRING_FUNC (32, guint32); -/** - * gst_byte_writer_put_uint8: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a unsigned 8 bit integer to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_uint16_be: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a unsigned big endian 16 bit integer to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_uint24_be: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a unsigned big endian 24 bit integer to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_uint32_be: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a unsigned big endian 32 bit integer to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_uint64_be: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a unsigned big endian 64 bit integer to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_uint16_le: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a unsigned little endian 16 bit integer to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_uint24_le: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a unsigned little endian 24 bit integer to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_uint32_le: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a unsigned little endian 32 bit integer to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_uint64_le: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a unsigned little endian 64 bit integer to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_int8: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a signed 8 bit integer to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_int16_be: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a signed big endian 16 bit integer to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_int24_be: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a signed big endian 24 bit integer to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_int32_be: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a signed big endian 32 bit integer to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_int64_be: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a signed big endian 64 bit integer to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_int16_le: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a signed little endian 16 bit integer to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_int24_le: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a signed little endian 24 bit integer to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_int32_le: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a signed little endian 32 bit integer to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_int64_le: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a signed little endian 64 bit integer to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_float32_be: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a big endian 32 bit float to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_float64_be: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a big endian 64 bit float to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_float32_le: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a little endian 32 bit float to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_float64_le: - * @writer: #GstByteWriter instance - * @val: Value to write - * - * Writes a little endian 64 bit float to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_string_utf8: - * @writer: #GstByteWriter instance - * @data: (transfer none): UTF8 string to write - * - * Writes a NUL-terminated UTF8 string to @writer (including the terminator). - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_string_utf16: - * @writer: #GstByteWriter instance - * @data: (transfer none) (array zero-terminated=1): UTF16 string to write - * - * Writes a NUL-terminated UTF16 string to @writer (including the terminator). - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_string_utf32: - * @writer: #GstByteWriter instance - * @data: (transfer none) (array zero-terminated=1): UTF32 string to write - * - * Writes a NUL-terminated UTF32 string to @writer (including the terminator). - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_put_data: - * @writer: #GstByteWriter instance - * @data: (transfer none) (array length=size): Data to write - * @size: Size of @data in bytes - * - * Writes @size bytes of @data to @writer. - * - * Returns: %TRUE if the value could be written - */ -/** - * gst_byte_writer_fill: - * @writer: #GstByteWriter instance - * @value: Value to be written - * @size: Number of bytes to be written - * - * Writes @size bytes containing @value to @writer. - * - * Returns: %TRUE if the value could be written - */ - -/** - * gst_byte_writer_put_buffer: - * @writer: #GstByteWriter instance - * @buffer: (transfer none): source #GstBuffer - * @offset: offset to copy from - * @size: total size to copy. If -1, all data is copied - * - * Writes @size bytes of @data to @writer. - * - * Returns: %TRUE if the data could be written - * - */ diff --git a/libs/gst/base/gstbytewriter.h b/libs/gst/base/gstbytewriter.h deleted file mode 100644 index 365c7742d7..0000000000 --- a/libs/gst/base/gstbytewriter.h +++ /dev/null @@ -1,468 +0,0 @@ -/* GStreamer byte writer - * - * 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., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_BYTE_WRITER_H__ -#define __GST_BYTE_WRITER_H__ - -#include <gst/gst.h> -#include <gst/base/gstbytereader.h> - -#include <string.h> - -G_BEGIN_DECLS - -#define GST_BYTE_WRITER(writer) ((GstByteWriter *) (writer)) - -/** - * GstByteWriter: - * @parent: #GstByteReader parent - * @alloc_size: Allocation size of the data - * @fixed: If %TRUE no reallocations are allowed - * @owned: If %FALSE no reallocations are allowed and copies of data are returned - * - * A byte writer instance. - */ -typedef struct { - GstByteReader parent; - - guint alloc_size; - - gboolean fixed; - gboolean owned; - - /* < private > */ - gpointer _gst_reserved[GST_PADDING]; -} GstByteWriter; - -GST_BASE_API -GstByteWriter * gst_byte_writer_new (void) G_GNUC_MALLOC; - -GST_BASE_API -GstByteWriter * gst_byte_writer_new_with_size (guint size, gboolean fixed) G_GNUC_MALLOC; - -GST_BASE_API -GstByteWriter * gst_byte_writer_new_with_data (guint8 *data, guint size, gboolean initialized) G_GNUC_MALLOC; - -GST_BASE_API -void gst_byte_writer_init (GstByteWriter *writer); - -GST_BASE_API -void gst_byte_writer_init_with_size (GstByteWriter *writer, guint size, gboolean fixed); - -GST_BASE_API -void gst_byte_writer_init_with_data (GstByteWriter *writer, guint8 *data, - guint size, gboolean initialized); -GST_BASE_API -void gst_byte_writer_free (GstByteWriter *writer); - -GST_BASE_API -guint8 * gst_byte_writer_free_and_get_data (GstByteWriter *writer); - -GST_BASE_API -GstBuffer * gst_byte_writer_free_and_get_buffer (GstByteWriter *writer) G_GNUC_MALLOC; - -GST_BASE_API -void gst_byte_writer_reset (GstByteWriter *writer); - -GST_BASE_API -guint8 * gst_byte_writer_reset_and_get_data (GstByteWriter *writer); - -GST_BASE_API -GstBuffer * gst_byte_writer_reset_and_get_buffer (GstByteWriter *writer) G_GNUC_MALLOC; - -/** - * gst_byte_writer_get_pos: - * @writer: #GstByteWriter instance - * - * Returns: The current position of the read/write cursor - */ -/** - * gst_byte_writer_set_pos: - * @writer: #GstByteWriter instance - * @pos: new position - * - * Sets the current read/write cursor of @writer. The new position - * can only be between 0 and the current size. - * - * Returns: %TRUE if the new position could be set - */ -/** - * gst_byte_writer_get_size: - * @writer: #GstByteWriter instance - * - * Returns: The current, initialized size of the data - */ -static inline guint -gst_byte_writer_get_pos (const GstByteWriter *writer) -{ - return gst_byte_reader_get_pos ((const GstByteReader *) writer); -} - -static inline gboolean -gst_byte_writer_set_pos (GstByteWriter *writer, guint pos) -{ - return gst_byte_reader_set_pos (GST_BYTE_READER (writer), pos); -} - -static inline guint -gst_byte_writer_get_size (const GstByteWriter *writer) -{ - return gst_byte_reader_get_size ((const GstByteReader *) writer); -} - -GST_BASE_API -guint gst_byte_writer_get_remaining (const GstByteWriter *writer); - -GST_BASE_API -gboolean gst_byte_writer_ensure_free_space (GstByteWriter *writer, guint size); - -GST_BASE_API -gboolean gst_byte_writer_put_uint8 (GstByteWriter *writer, guint8 val); - -GST_BASE_API -gboolean gst_byte_writer_put_int8 (GstByteWriter *writer, gint8 val); - -GST_BASE_API -gboolean gst_byte_writer_put_uint16_be (GstByteWriter *writer, guint16 val); - -GST_BASE_API -gboolean gst_byte_writer_put_uint16_le (GstByteWriter *writer, guint16 val); - -GST_BASE_API -gboolean gst_byte_writer_put_int16_be (GstByteWriter *writer, gint16 val); - -GST_BASE_API -gboolean gst_byte_writer_put_int16_le (GstByteWriter *writer, gint16 val); - -GST_BASE_API -gboolean gst_byte_writer_put_uint24_be (GstByteWriter *writer, guint32 val); - -GST_BASE_API -gboolean gst_byte_writer_put_uint24_le (GstByteWriter *writer, guint32 val); - -GST_BASE_API -gboolean gst_byte_writer_put_int24_be (GstByteWriter *writer, gint32 val); - -GST_BASE_API -gboolean gst_byte_writer_put_int24_le (GstByteWriter *writer, gint32 val); - -GST_BASE_API -gboolean gst_byte_writer_put_uint32_be (GstByteWriter *writer, guint32 val); - -GST_BASE_API -gboolean gst_byte_writer_put_uint32_le (GstByteWriter *writer, guint32 val); - -GST_BASE_API -gboolean gst_byte_writer_put_int32_be (GstByteWriter *writer, gint32 val); - -GST_BASE_API -gboolean gst_byte_writer_put_int32_le (GstByteWriter *writer, gint32 val); - -GST_BASE_API -gboolean gst_byte_writer_put_uint64_be (GstByteWriter *writer, guint64 val); - -GST_BASE_API -gboolean gst_byte_writer_put_uint64_le (GstByteWriter *writer, guint64 val); - -GST_BASE_API -gboolean gst_byte_writer_put_int64_be (GstByteWriter *writer, gint64 val); - -GST_BASE_API -gboolean gst_byte_writer_put_int64_le (GstByteWriter *writer, gint64 val); - -GST_BASE_API -gboolean gst_byte_writer_put_float32_be (GstByteWriter *writer, gfloat val); - -GST_BASE_API -gboolean gst_byte_writer_put_float32_le (GstByteWriter *writer, gfloat val); - -GST_BASE_API -gboolean gst_byte_writer_put_float64_be (GstByteWriter *writer, gdouble val); - -GST_BASE_API -gboolean gst_byte_writer_put_float64_le (GstByteWriter *writer, gdouble val); - -GST_BASE_API -gboolean gst_byte_writer_put_data (GstByteWriter *writer, const guint8 *data, guint size); - -GST_BASE_API -gboolean gst_byte_writer_fill (GstByteWriter *writer, guint8 value, guint size); - -GST_BASE_API -gboolean gst_byte_writer_put_string_utf8 (GstByteWriter *writer, const gchar *data); - -GST_BASE_API -gboolean gst_byte_writer_put_string_utf16 (GstByteWriter *writer, const guint16 *data); - -GST_BASE_API -gboolean gst_byte_writer_put_string_utf32 (GstByteWriter *writer, const guint32 *data); -gboolean gst_byte_writer_put_buffer (GstByteWriter *writer, GstBuffer * buffer, gsize offset, gssize size); - -/** - * gst_byte_writer_put_string: - * @writer: #GstByteWriter instance - * @data: (in) (array zero-terminated=1): Null terminated string - * - * Write a NUL-terminated string to @writer (including the terminator). The - * string is assumed to be in an 8-bit encoding (e.g. ASCII,UTF-8 or - * ISO-8859-1). - * - * Returns: %TRUE if the string could be written - */ -#define gst_byte_writer_put_string(writer, data) \ - gst_byte_writer_put_string_utf8(writer, data) - -static inline guint -_gst_byte_writer_next_pow2 (guint n) -{ - guint ret = 16; - - /* We start with 16, smaller allocations make no sense */ - - while (ret < n && ret > 0) - ret <<= 1; - - return ret ? ret : n; -} - -static inline gboolean -_gst_byte_writer_ensure_free_space_inline (GstByteWriter * writer, guint size) -{ - gpointer data; - - if (G_LIKELY (size <= writer->alloc_size - writer->parent.byte)) - return TRUE; - if (G_UNLIKELY (writer->fixed || !writer->owned)) - return FALSE; - if (G_UNLIKELY (writer->parent.byte > G_MAXUINT - size)) - return FALSE; - - writer->alloc_size = _gst_byte_writer_next_pow2 (writer->parent.byte + size); - data = g_try_realloc ((guint8 *) writer->parent.data, writer->alloc_size); - if (G_UNLIKELY (data == NULL)) - return FALSE; - - writer->parent.data = (guint8 *) data; - - return TRUE; -} - -#define __GST_BYTE_WRITER_CREATE_WRITE_FUNC(bits,type,name,write_func) \ -static inline void \ -gst_byte_writer_put_##name##_unchecked (GstByteWriter *writer, type val) \ -{ \ - guint8 *write_data; \ - \ - write_data = (guint8 *) writer->parent.data + writer->parent.byte; \ - write_func (write_data, val); \ - writer->parent.byte += bits/8; \ - writer->parent.size = MAX (writer->parent.size, writer->parent.byte); \ -} \ -\ -static inline gboolean \ -_gst_byte_writer_put_##name##_inline (GstByteWriter *writer, type val) \ -{ \ - g_return_val_if_fail (writer != NULL, FALSE); \ - \ - if (G_UNLIKELY (!_gst_byte_writer_ensure_free_space_inline(writer, bits/8))) \ - return FALSE; \ - \ - gst_byte_writer_put_##name##_unchecked (writer, val); \ - \ - return TRUE; \ -} - -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (8, guint8, uint8, GST_WRITE_UINT8) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (8, gint8, int8, GST_WRITE_UINT8) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (16, guint16, uint16_le, GST_WRITE_UINT16_LE) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (16, guint16, uint16_be, GST_WRITE_UINT16_BE) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (16, gint16, int16_le, GST_WRITE_UINT16_LE) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (16, gint16, int16_be, GST_WRITE_UINT16_BE) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (24, guint32, uint24_le, GST_WRITE_UINT24_LE) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (24, guint32, uint24_be, GST_WRITE_UINT24_BE) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (24, gint32, int24_le, GST_WRITE_UINT24_LE) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (24, gint32, int24_be, GST_WRITE_UINT24_BE) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (32, guint32, uint32_le, GST_WRITE_UINT32_LE) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (32, guint32, uint32_be, GST_WRITE_UINT32_BE) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (32, gint32, int32_le, GST_WRITE_UINT32_LE) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (32, gint32, int32_be, GST_WRITE_UINT32_BE) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (64, guint64, uint64_le, GST_WRITE_UINT64_LE) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (64, guint64, uint64_be, GST_WRITE_UINT64_BE) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (64, gint64, int64_le, GST_WRITE_UINT64_LE) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (64, gint64, int64_be, GST_WRITE_UINT64_BE) - -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (32, gfloat, float32_be, GST_WRITE_FLOAT_BE) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (32, gfloat, float32_le, GST_WRITE_FLOAT_LE) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (64, gdouble, float64_be, GST_WRITE_DOUBLE_BE) -__GST_BYTE_WRITER_CREATE_WRITE_FUNC (64, gdouble, float64_le, GST_WRITE_DOUBLE_LE) - -#undef __GST_BYTE_WRITER_CREATE_WRITE_FUNC - -static inline void -gst_byte_writer_put_data_unchecked (GstByteWriter * writer, const guint8 * data, - guint size) -{ - memcpy ((guint8 *) & writer->parent.data[writer->parent.byte], data, size); - writer->parent.byte += size; - writer->parent.size = MAX (writer->parent.size, writer->parent.byte); -} - -static inline gboolean -_gst_byte_writer_put_data_inline (GstByteWriter * writer, const guint8 * data, - guint size) -{ - g_return_val_if_fail (writer != NULL, FALSE); - - if (G_UNLIKELY (!_gst_byte_writer_ensure_free_space_inline (writer, size))) - return FALSE; - - gst_byte_writer_put_data_unchecked (writer, data, size); - - return TRUE; -} - -static inline void -gst_byte_writer_fill_unchecked (GstByteWriter * writer, guint8 value, guint size) -{ - memset ((guint8 *) & writer->parent.data[writer->parent.byte], value, size); - writer->parent.byte += size; - writer->parent.size = MAX (writer->parent.size, writer->parent.byte); -} - -static inline gboolean -_gst_byte_writer_fill_inline (GstByteWriter * writer, guint8 value, guint size) -{ - g_return_val_if_fail (writer != NULL, FALSE); - - if (G_UNLIKELY (!_gst_byte_writer_ensure_free_space_inline (writer, size))) - return FALSE; - - gst_byte_writer_fill_unchecked (writer, value, size); - - return TRUE; -} - -static inline void -gst_byte_writer_put_buffer_unchecked (GstByteWriter * writer, GstBuffer * buffer, - gsize offset, gssize size) -{ - if (size == -1) { - size = gst_buffer_get_size (buffer); - - if (offset >= (gsize) size) - return; - - size -= offset; - } - - gst_buffer_extract (buffer, offset, - (guint8 *) & writer->parent.data[writer->parent.byte], size); - writer->parent.byte += size; - writer->parent.size = MAX (writer->parent.size, writer->parent.byte); -} - -static inline gboolean -_gst_byte_writer_put_buffer_inline (GstByteWriter * writer, GstBuffer * buffer, - gsize offset, gssize size) -{ - g_return_val_if_fail (writer != NULL, FALSE); - g_return_val_if_fail (size >= -1, FALSE); - - if (size == -1) { - size = gst_buffer_get_size (buffer); - - if (offset >= (gsize) size) - return TRUE; - - size -= offset; - } - - if (G_UNLIKELY (!_gst_byte_writer_ensure_free_space_inline (writer, size))) - return FALSE; - - gst_byte_writer_put_buffer_unchecked (writer, buffer, offset, size); - - return TRUE; -} - -#ifndef GST_BYTE_WRITER_DISABLE_INLINES - -/* we use defines here so we can add the G_LIKELY() */ - -#define gst_byte_writer_ensure_free_space(writer, size) \ - G_LIKELY (_gst_byte_writer_ensure_free_space_inline (writer, size)) -#define gst_byte_writer_put_uint8(writer, val) \ - G_LIKELY (_gst_byte_writer_put_uint8_inline (writer, val)) -#define gst_byte_writer_put_int8(writer, val) \ - G_LIKELY (_gst_byte_writer_put_int8_inline (writer, val)) -#define gst_byte_writer_put_uint16_be(writer, val) \ - G_LIKELY (_gst_byte_writer_put_uint16_be_inline (writer, val)) -#define gst_byte_writer_put_uint16_le(writer, val) \ - G_LIKELY (_gst_byte_writer_put_uint16_le_inline (writer, val)) -#define gst_byte_writer_put_int16_be(writer, val) \ - G_LIKELY (_gst_byte_writer_put_int16_be_inline (writer, val)) -#define gst_byte_writer_put_int16_le(writer, val) \ - G_LIKELY (_gst_byte_writer_put_int16_le_inline (writer, val)) -#define gst_byte_writer_put_uint24_be(writer, val) \ - G_LIKELY (_gst_byte_writer_put_uint24_be_inline (writer, val)) -#define gst_byte_writer_put_uint24_le(writer, val) \ - G_LIKELY (_gst_byte_writer_put_uint24_le_inline (writer, val)) -#define gst_byte_writer_put_int24_be(writer, val) \ - G_LIKELY (_gst_byte_writer_put_int24_be_inline (writer, val)) -#define gst_byte_writer_put_int24_le(writer, val) \ - G_LIKELY (_gst_byte_writer_put_int24_le_inline (writer, val)) -#define gst_byte_writer_put_uint32_be(writer, val) \ - G_LIKELY (_gst_byte_writer_put_uint32_be_inline (writer, val)) -#define gst_byte_writer_put_uint32_le(writer, val) \ - G_LIKELY (_gst_byte_writer_put_uint32_le_inline (writer, val)) -#define gst_byte_writer_put_int32_be(writer, val) \ - G_LIKELY (_gst_byte_writer_put_int32_be_inline (writer, val)) -#define gst_byte_writer_put_int32_le(writer, val) \ - G_LIKELY (_gst_byte_writer_put_int32_le_inline (writer, val)) -#define gst_byte_writer_put_uint64_be(writer, val) \ - G_LIKELY (_gst_byte_writer_put_uint64_be_inline (writer, val)) -#define gst_byte_writer_put_uint64_le(writer, val) \ - G_LIKELY (_gst_byte_writer_put_uint64_le_inline (writer, val)) -#define gst_byte_writer_put_int64_be(writer, val) \ - G_LIKELY (_gst_byte_writer_put_int64_be_inline (writer, val)) -#define gst_byte_writer_put_int64_le(writer, val) \ - G_LIKELY (_gst_byte_writer_put_int64_le_inline (writer, val)) - -#define gst_byte_writer_put_float32_be(writer, val) \ - G_LIKELY (_gst_byte_writer_put_float32_be_inline (writer, val)) -#define gst_byte_writer_put_float32_le(writer, val) \ - G_LIKELY (_gst_byte_writer_put_float32_le_inline (writer, val)) -#define gst_byte_writer_put_float64_be(writer, val) \ - G_LIKELY (_gst_byte_writer_put_float64_be_inline (writer, val)) -#define gst_byte_writer_put_float64_le(writer, val) \ - G_LIKELY (_gst_byte_writer_put_float64_le_inline (writer, val)) - -#define gst_byte_writer_put_data(writer, data, size) \ - G_LIKELY (_gst_byte_writer_put_data_inline (writer, data, size)) -#define gst_byte_writer_fill(writer, val, size) \ - G_LIKELY (_gst_byte_writer_fill_inline (writer, val, size)) -#define gst_byte_writer_put_buffer(writer, buffer, offset, size) \ - G_LIKELY (_gst_byte_writer_put_buffer_inline (writer, buffer, offset, size)) - -#endif - -G_END_DECLS - -#endif /* __GST_BYTE_WRITER_H__ */ diff --git a/libs/gst/base/gstcollectpads.c b/libs/gst/base/gstcollectpads.c deleted file mode 100644 index b6f92159c0..0000000000 --- a/libs/gst/base/gstcollectpads.c +++ /dev/null @@ -1,2319 +0,0 @@ -/* GStreamer - * Copyright (C) 2005 Wim Taymans <wim@fluendo.com> - * Copyright (C) 2008 Mark Nauwelaerts <mnauw@users.sourceforge.net> - * Copyright (C) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * - * gstcollectpads.c: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ -/** - * SECTION:gstcollectpads - * @title: GstCollectPads - * @short_description: manages a set of pads that operate in collect mode - * - * Manages a set of pads that operate in collect mode. This means that control - * is given to the manager of this object when all pads have data. - * - * * Collectpads are created with gst_collect_pads_new(). A callback should then - * be installed with gst_collect_pads_set_function (). - * - * * Pads are added to the collection with gst_collect_pads_add_pad()/ - * gst_collect_pads_remove_pad(). The pad has to be a sinkpad. When added, - * the chain, event and query functions of the pad are overridden. The - * element_private of the pad is used to store private information for the - * collectpads. - * - * * For each pad, data is queued in the _chain function or by - * performing a pull_range. - * - * * When data is queued on all pads in waiting mode, the callback function is called. - * - * * Data can be dequeued from the pad with the gst_collect_pads_pop() method. - * One can peek at the data with the gst_collect_pads_peek() function. - * These functions will return %NULL if the pad received an EOS event. When all - * pads return %NULL from a gst_collect_pads_peek(), the element can emit an EOS - * event itself. - * - * * Data can also be dequeued in byte units using the gst_collect_pads_available(), - * gst_collect_pads_read_buffer() and gst_collect_pads_flush() calls. - * - * * Elements should call gst_collect_pads_start() and gst_collect_pads_stop() in - * their state change functions to start and stop the processing of the collectpads. - * The gst_collect_pads_stop() call should be called before calling the parent - * element state change function in the PAUSED_TO_READY state change to ensure - * no pad is blocked and the element can finish streaming. - * - * * gst_collect_pads_set_waiting() sets a pad to waiting or non-waiting mode. - * CollectPads element is not waiting for data to be collected on non-waiting pads. - * Thus these pads may but need not have data when the callback is called. - * All pads are in waiting mode by default. - * - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include <gst/gst_private.h> - -#include "gstcollectpads.h" - -#include "../../../gst/glib-compat-private.h" - -GST_DEBUG_CATEGORY_STATIC (collect_pads_debug); -#define GST_CAT_DEFAULT collect_pads_debug - -struct _GstCollectDataPrivate -{ - /* refcounting for struct, and destroy callback */ - GstCollectDataDestroyNotify destroy_notify; - gint refcount; -}; - -struct _GstCollectPadsPrivate -{ - /* with LOCK and/or STREAM_LOCK */ - gboolean started; - - /* with STREAM_LOCK */ - guint32 cookie; /* pad_list cookie */ - guint numpads; /* number of pads in @data */ - guint queuedpads; /* number of pads with a buffer */ - guint eospads; /* number of pads that are EOS */ - GstClockTime earliest_time; /* Current earliest time */ - GstCollectData *earliest_data; /* Pad data for current earliest time */ - - /* with LOCK */ - GSList *pad_list; /* list of GstCollectData* */ - guint32 pad_cookie; /* updated cookie */ - - GstCollectPadsFunction func; /* function and user_data for callback */ - gpointer user_data; - GstCollectPadsBufferFunction buffer_func; /* function and user_data for buffer callback */ - gpointer buffer_user_data; - GstCollectPadsCompareFunction compare_func; - gpointer compare_user_data; - GstCollectPadsEventFunction event_func; /* function and data for event callback */ - gpointer event_user_data; - GstCollectPadsQueryFunction query_func; - gpointer query_user_data; - GstCollectPadsClipFunction clip_func; - gpointer clip_user_data; - GstCollectPadsFlushFunction flush_func; - gpointer flush_user_data; - - /* no other lock needed */ - GMutex evt_lock; /* these make up sort of poor man's event signaling */ - GCond evt_cond; - guint32 evt_cookie; - - gboolean seeking; - gboolean pending_flush_start; - gboolean pending_flush_stop; -}; - -#define parent_class gst_collect_pads_parent_class -G_DEFINE_TYPE_WITH_PRIVATE (GstCollectPads, gst_collect_pads, GST_TYPE_OBJECT); - -static void gst_collect_pads_clear (GstCollectPads * pads, - GstCollectData * data); -static GstFlowReturn gst_collect_pads_chain (GstPad * pad, GstObject * parent, - GstBuffer * buffer); -static gboolean gst_collect_pads_event (GstPad * pad, GstObject * parent, - GstEvent * event); -static gboolean gst_collect_pads_query (GstPad * pad, GstObject * parent, - GstQuery * query); -static void gst_collect_pads_finalize (GObject * object); -static GstFlowReturn gst_collect_pads_default_collected (GstCollectPads * - pads, gpointer user_data); -static gint gst_collect_pads_default_compare_func (GstCollectPads * pads, - GstCollectData * data1, GstClockTime timestamp1, GstCollectData * data2, - GstClockTime timestamp2, gpointer user_data); -static gboolean gst_collect_pads_recalculate_full (GstCollectPads * pads); -static void ref_data (GstCollectData * data); -static void unref_data (GstCollectData * data); - -static gboolean gst_collect_pads_event_default_internal (GstCollectPads * - pads, GstCollectData * data, GstEvent * event, gpointer user_data); -static gboolean gst_collect_pads_query_default_internal (GstCollectPads * - pads, GstCollectData * data, GstQuery * query, gpointer user_data); - - -/* Some properties are protected by LOCK, others by STREAM_LOCK - * However, manipulating either of these partitions may require - * to signal/wake a _WAIT, so use a separate (sort of) event to prevent races - * Alternative implementations are possible, e.g. some low-level re-implementing - * of the 2 above locks to drop both of them atomically when going into _WAIT. - */ -#define GST_COLLECT_PADS_GET_EVT_COND(pads) (&((GstCollectPads *)pads)->priv->evt_cond) -#define GST_COLLECT_PADS_GET_EVT_LOCK(pads) (&((GstCollectPads *)pads)->priv->evt_lock) -#define GST_COLLECT_PADS_EVT_WAIT(pads, cookie) G_STMT_START { \ - g_mutex_lock (GST_COLLECT_PADS_GET_EVT_LOCK (pads)); \ - /* should work unless a lot of event'ing and thread starvation */\ - while (cookie == ((GstCollectPads *) pads)->priv->evt_cookie) \ - g_cond_wait (GST_COLLECT_PADS_GET_EVT_COND (pads), \ - GST_COLLECT_PADS_GET_EVT_LOCK (pads)); \ - cookie = ((GstCollectPads *) pads)->priv->evt_cookie; \ - g_mutex_unlock (GST_COLLECT_PADS_GET_EVT_LOCK (pads)); \ -} G_STMT_END -#define GST_COLLECT_PADS_EVT_WAIT_TIMED(pads, cookie, timeout) G_STMT_START { \ - gint64 end_time = g_get_monotonic_time () + timeout; \ - \ - g_mutex_lock (GST_COLLECT_PADS_GET_EVT_LOCK (pads)); \ - /* should work unless a lot of event'ing and thread starvation */\ - while (cookie == ((GstCollectPads *) pads)->priv->evt_cookie) \ - g_cond_wait_until (GST_COLLECT_PADS_GET_EVT_COND (pads), \ - GST_COLLECT_PADS_GET_EVT_LOCK (pads), end_time); \ - cookie = ((GstCollectPads *) pads)->priv->evt_cookie; \ - g_mutex_unlock (GST_COLLECT_PADS_GET_EVT_LOCK (pads)); \ -} G_STMT_END -#define GST_COLLECT_PADS_EVT_BROADCAST(pads) G_STMT_START { \ - g_mutex_lock (GST_COLLECT_PADS_GET_EVT_LOCK (pads)); \ - /* never mind wrap-around */ \ - ++(((GstCollectPads *) pads)->priv->evt_cookie); \ - g_cond_broadcast (GST_COLLECT_PADS_GET_EVT_COND (pads)); \ - g_mutex_unlock (GST_COLLECT_PADS_GET_EVT_LOCK (pads)); \ -} G_STMT_END -#define GST_COLLECT_PADS_EVT_INIT(cookie) G_STMT_START { \ - g_mutex_lock (GST_COLLECT_PADS_GET_EVT_LOCK (pads)); \ - cookie = ((GstCollectPads *) pads)->priv->evt_cookie; \ - g_mutex_unlock (GST_COLLECT_PADS_GET_EVT_LOCK (pads)); \ -} G_STMT_END - -static void -gst_collect_pads_class_init (GstCollectPadsClass * klass) -{ - GObjectClass *gobject_class = (GObjectClass *) klass; - - GST_DEBUG_CATEGORY_INIT (collect_pads_debug, "collectpads", 0, - "GstCollectPads"); - - gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_collect_pads_finalize); -} - -static void -gst_collect_pads_init (GstCollectPads * pads) -{ - pads->priv = gst_collect_pads_get_instance_private (pads); - - pads->data = NULL; - pads->priv->cookie = 0; - pads->priv->numpads = 0; - pads->priv->queuedpads = 0; - pads->priv->eospads = 0; - pads->priv->started = FALSE; - - g_rec_mutex_init (&pads->stream_lock); - - pads->priv->func = gst_collect_pads_default_collected; - pads->priv->user_data = NULL; - pads->priv->event_func = NULL; - pads->priv->event_user_data = NULL; - - /* members for default muxing */ - pads->priv->buffer_func = NULL; - pads->priv->buffer_user_data = NULL; - pads->priv->compare_func = gst_collect_pads_default_compare_func; - pads->priv->compare_user_data = NULL; - pads->priv->earliest_data = NULL; - pads->priv->earliest_time = GST_CLOCK_TIME_NONE; - - pads->priv->event_func = gst_collect_pads_event_default_internal; - pads->priv->query_func = gst_collect_pads_query_default_internal; - - /* members to manage the pad list */ - pads->priv->pad_cookie = 0; - pads->priv->pad_list = NULL; - - /* members for event */ - g_mutex_init (&pads->priv->evt_lock); - g_cond_init (&pads->priv->evt_cond); - pads->priv->evt_cookie = 0; - - pads->priv->seeking = FALSE; - pads->priv->pending_flush_start = FALSE; - pads->priv->pending_flush_stop = FALSE; -} - -static void -gst_collect_pads_finalize (GObject * object) -{ - GstCollectPads *pads = GST_COLLECT_PADS (object); - - GST_DEBUG_OBJECT (object, "finalize"); - - g_rec_mutex_clear (&pads->stream_lock); - - g_cond_clear (&pads->priv->evt_cond); - g_mutex_clear (&pads->priv->evt_lock); - - /* Remove pads and free pads list */ - g_slist_foreach (pads->priv->pad_list, (GFunc) unref_data, NULL); - g_slist_foreach (pads->data, (GFunc) unref_data, NULL); - g_slist_free (pads->data); - g_slist_free (pads->priv->pad_list); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -/** - * gst_collect_pads_new: - * - * Create a new instance of #GstCollectPads. - * - * MT safe. - * - * Returns: (transfer full): a new #GstCollectPads, or %NULL in case of an error. - */ -GstCollectPads * -gst_collect_pads_new (void) -{ - GstCollectPads *newcoll; - - newcoll = g_object_new (GST_TYPE_COLLECT_PADS, NULL); - - /* clear floating flag */ - gst_object_ref_sink (newcoll); - - return newcoll; -} - -/* Must be called with GstObject lock! */ -static void -gst_collect_pads_set_buffer_function_locked (GstCollectPads * pads, - GstCollectPadsBufferFunction func, gpointer user_data) -{ - pads->priv->buffer_func = func; - pads->priv->buffer_user_data = user_data; -} - -/** - * gst_collect_pads_set_buffer_function: - * @pads: the collectpads to use - * @func: (scope call): the function to set - * @user_data: (closure): user data passed to the function - * - * Set the callback function and user data that will be called with - * the oldest buffer when all pads have been collected, or %NULL on EOS. - * If a buffer is passed, the callback owns a reference and must unref - * it. - * - * MT safe. - */ -void -gst_collect_pads_set_buffer_function (GstCollectPads * pads, - GstCollectPadsBufferFunction func, gpointer user_data) -{ - g_return_if_fail (pads != NULL); - g_return_if_fail (GST_IS_COLLECT_PADS (pads)); - - GST_OBJECT_LOCK (pads); - gst_collect_pads_set_buffer_function_locked (pads, func, user_data); - GST_OBJECT_UNLOCK (pads); -} - -/** - * gst_collect_pads_set_compare_function: - * @pads: the pads to use - * @func: (scope call): the function to set - * @user_data: (closure): user data passed to the function - * - * Set the timestamp comparison function. - * - * MT safe. - */ -/* NOTE allowing to change comparison seems not advisable; -no known use-case, and collaboration with default algorithm is unpredictable. -If custom comparing/operation is needed, just use a collect function of -your own */ -void -gst_collect_pads_set_compare_function (GstCollectPads * pads, - GstCollectPadsCompareFunction func, gpointer user_data) -{ - g_return_if_fail (pads != NULL); - g_return_if_fail (GST_IS_COLLECT_PADS (pads)); - - GST_OBJECT_LOCK (pads); - pads->priv->compare_func = func; - pads->priv->compare_user_data = user_data; - GST_OBJECT_UNLOCK (pads); -} - -/** - * gst_collect_pads_set_function: - * @pads: the collectpads to use - * @func: (scope call): the function to set - * @user_data: user data passed to the function - * - * CollectPads provides a default collection algorithm that will determine - * the oldest buffer available on all of its pads, and then delegate - * to a configured callback. - * However, if circumstances are more complicated and/or more control - * is desired, this sets a callback that will be invoked instead when - * all the pads added to the collection have buffers queued. - * Evidently, this callback is not compatible with - * gst_collect_pads_set_buffer_function() callback. - * If this callback is set, the former will be unset. - * - * MT safe. - */ -void -gst_collect_pads_set_function (GstCollectPads * pads, - GstCollectPadsFunction func, gpointer user_data) -{ - g_return_if_fail (pads != NULL); - g_return_if_fail (GST_IS_COLLECT_PADS (pads)); - - GST_OBJECT_LOCK (pads); - pads->priv->func = func; - pads->priv->user_data = user_data; - gst_collect_pads_set_buffer_function_locked (pads, NULL, NULL); - GST_OBJECT_UNLOCK (pads); -} - -static void -ref_data (GstCollectData * data) -{ - g_assert (data != NULL); - - g_atomic_int_inc (&(data->priv->refcount)); -} - -static void -unref_data (GstCollectData * data) -{ - g_assert (data != NULL); - g_assert (data->priv->refcount > 0); - - if (!g_atomic_int_dec_and_test (&(data->priv->refcount))) - return; - - if (data->priv->destroy_notify) - data->priv->destroy_notify (data); - - gst_object_unref (data->pad); - if (data->buffer) { - gst_buffer_unref (data->buffer); - } - g_free (data->priv); - g_free (data); -} - -/** - * gst_collect_pads_set_event_function: - * @pads: the collectpads to use - * @func: (scope call): the function to set - * @user_data: user data passed to the function - * - * Set the event callback function and user data that will be called when - * collectpads has received an event originating from one of the collected - * pads. If the event being processed is a serialized one, this callback is - * called with @pads STREAM_LOCK held, otherwise not. As this lock should be - * held when calling a number of CollectPads functions, it should be acquired - * if so (unusually) needed. - * - * MT safe. - */ -void -gst_collect_pads_set_event_function (GstCollectPads * pads, - GstCollectPadsEventFunction func, gpointer user_data) -{ - g_return_if_fail (pads != NULL); - g_return_if_fail (GST_IS_COLLECT_PADS (pads)); - - GST_OBJECT_LOCK (pads); - pads->priv->event_func = func; - pads->priv->event_user_data = user_data; - GST_OBJECT_UNLOCK (pads); -} - -/** - * gst_collect_pads_set_query_function: - * @pads: the collectpads to use - * @func: (scope call): the function to set - * @user_data: user data passed to the function - * - * Set the query callback function and user data that will be called after - * collectpads has received a query originating from one of the collected - * pads. If the query being processed is a serialized one, this callback is - * called with @pads STREAM_LOCK held, otherwise not. As this lock should be - * held when calling a number of CollectPads functions, it should be acquired - * if so (unusually) needed. - * - * MT safe. - */ -void -gst_collect_pads_set_query_function (GstCollectPads * pads, - GstCollectPadsQueryFunction func, gpointer user_data) -{ - g_return_if_fail (pads != NULL); - g_return_if_fail (GST_IS_COLLECT_PADS (pads)); - - GST_OBJECT_LOCK (pads); - pads->priv->query_func = func; - pads->priv->query_user_data = user_data; - GST_OBJECT_UNLOCK (pads); -} - -/** -* gst_collect_pads_clip_running_time: -* @pads: the collectpads to use -* @cdata: collect data of corresponding pad -* @buf: buffer being clipped -* @outbuf: (allow-none) (out): output buffer with running time, or NULL if clipped -* @user_data: user data (unused) -* -* Convenience clipping function that converts incoming buffer's timestamp -* to running time, or clips the buffer if outside configured segment. -* -* Since 1.6, this clipping function also sets the DTS parameter of the -* GstCollectData structure. This version of the running time DTS can be -* negative. G_MININT64 is used to indicate invalid value. -*/ -GstFlowReturn -gst_collect_pads_clip_running_time (GstCollectPads * pads, - GstCollectData * cdata, GstBuffer * buf, GstBuffer ** outbuf, - gpointer user_data) -{ - *outbuf = buf; - - /* invalid left alone and passed */ - if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DTS_OR_PTS (buf)))) { - GstClockTime time; - GstClockTime buf_dts, abs_dts; - gint dts_sign; - - time = GST_BUFFER_PTS (buf); - - if (GST_CLOCK_TIME_IS_VALID (time)) { - time = - gst_segment_to_running_time (&cdata->segment, GST_FORMAT_TIME, time); - if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) { - GST_DEBUG_OBJECT (cdata->pad, "clipping buffer on pad outside segment %" - GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_PTS (buf))); - gst_buffer_unref (buf); - *outbuf = NULL; - return GST_FLOW_OK; - } - } - - GST_LOG_OBJECT (cdata->pad, "buffer pts %" GST_TIME_FORMAT " -> %" - GST_TIME_FORMAT " running time", - GST_TIME_ARGS (GST_BUFFER_PTS (buf)), GST_TIME_ARGS (time)); - *outbuf = gst_buffer_make_writable (buf); - GST_BUFFER_PTS (*outbuf) = time; - - dts_sign = gst_segment_to_running_time_full (&cdata->segment, - GST_FORMAT_TIME, GST_BUFFER_DTS (*outbuf), &abs_dts); - buf_dts = GST_BUFFER_DTS (*outbuf); - if (dts_sign > 0) { - GST_BUFFER_DTS (*outbuf) = abs_dts; - GST_COLLECT_PADS_DTS (cdata) = abs_dts; - } else if (dts_sign < 0) { - GST_BUFFER_DTS (*outbuf) = GST_CLOCK_TIME_NONE; - GST_COLLECT_PADS_DTS (cdata) = -((gint64) abs_dts); - } else { - GST_BUFFER_DTS (*outbuf) = GST_CLOCK_TIME_NONE; - GST_COLLECT_PADS_DTS (cdata) = GST_CLOCK_STIME_NONE; - } - - GST_LOG_OBJECT (cdata->pad, "buffer dts %" GST_TIME_FORMAT " -> %" - GST_STIME_FORMAT " running time", GST_TIME_ARGS (buf_dts), - GST_STIME_ARGS (GST_COLLECT_PADS_DTS (cdata))); - } - - return GST_FLOW_OK; -} - -/** - * gst_collect_pads_set_clip_function: - * @pads: the collectpads to use - * @clipfunc: (scope call): clip function to install - * @user_data: user data to pass to @clip_func - * - * Install a clipping function that is called right after a buffer is received - * on a pad managed by @pads. See #GstCollectPadsClipFunction for more info. - */ -void -gst_collect_pads_set_clip_function (GstCollectPads * pads, - GstCollectPadsClipFunction clipfunc, gpointer user_data) -{ - g_return_if_fail (pads != NULL); - g_return_if_fail (GST_IS_COLLECT_PADS (pads)); - - pads->priv->clip_func = clipfunc; - pads->priv->clip_user_data = user_data; -} - -/** - * gst_collect_pads_set_flush_function: - * @pads: the collectpads to use - * @func: (scope call): flush function to install - * @user_data: user data to pass to @func - * - * Install a flush function that is called when the internal - * state of all pads should be flushed as part of flushing seek - * handling. See #GstCollectPadsFlushFunction for more info. - * - * Since: 1.4 - */ -void -gst_collect_pads_set_flush_function (GstCollectPads * pads, - GstCollectPadsFlushFunction func, gpointer user_data) -{ - g_return_if_fail (pads != NULL); - g_return_if_fail (GST_IS_COLLECT_PADS (pads)); - - pads->priv->flush_func = func; - pads->priv->flush_user_data = user_data; -} - -/** - * gst_collect_pads_add_pad: - * @pads: the collectpads to use - * @pad: (transfer none): the pad to add - * @size: the size of the returned #GstCollectData structure - * @destroy_notify: (scope async): function to be called before the returned - * #GstCollectData structure is freed - * @lock: whether to lock this pad in usual waiting state - * - * Add a pad to the collection of collect pads. The pad has to be - * a sinkpad. The refcount of the pad is incremented. Use - * gst_collect_pads_remove_pad() to remove the pad from the collection - * again. - * - * You specify a size for the returned #GstCollectData structure - * so that you can use it to store additional information. - * - * You can also specify a #GstCollectDataDestroyNotify that will be called - * just before the #GstCollectData structure is freed. It is passed the - * pointer to the structure and should free any custom memory and resources - * allocated for it. - * - * Keeping a pad locked in waiting state is only relevant when using - * the default collection algorithm (providing the oldest buffer). - * It ensures a buffer must be available on this pad for a collection - * to take place. This is of typical use to a muxer element where - * non-subtitle streams should always be in waiting state, - * e.g. to assure that caps information is available on all these streams - * when initial headers have to be written. - * - * The pad will be automatically activated in push mode when @pads is - * started. - * - * MT safe. - * - * Returns: (nullable) (transfer none): a new #GstCollectData to identify the - * new pad. Or %NULL if wrong parameters are supplied. - */ -GstCollectData * -gst_collect_pads_add_pad (GstCollectPads * pads, GstPad * pad, guint size, - GstCollectDataDestroyNotify destroy_notify, gboolean lock) -{ - GstCollectData *data; - - g_return_val_if_fail (pads != NULL, NULL); - g_return_val_if_fail (GST_IS_COLLECT_PADS (pads), NULL); - g_return_val_if_fail (pad != NULL, NULL); - g_return_val_if_fail (GST_PAD_IS_SINK (pad), NULL); - g_return_val_if_fail (size >= sizeof (GstCollectData), NULL); - - GST_DEBUG_OBJECT (pads, "adding pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - - data = g_malloc0 (size); - data->priv = g_new0 (GstCollectDataPrivate, 1); - data->collect = pads; - data->pad = gst_object_ref (pad); - data->buffer = NULL; - data->pos = 0; - gst_segment_init (&data->segment, GST_FORMAT_UNDEFINED); - data->state = GST_COLLECT_PADS_STATE_WAITING; - data->state |= lock ? GST_COLLECT_PADS_STATE_LOCKED : 0; - data->priv->refcount = 1; - data->priv->destroy_notify = destroy_notify; - data->ABI.abi.dts = G_MININT64; - - GST_OBJECT_LOCK (pads); - GST_OBJECT_LOCK (pad); - gst_pad_set_element_private (pad, data); - GST_OBJECT_UNLOCK (pad); - pads->priv->pad_list = g_slist_append (pads->priv->pad_list, data); - gst_pad_set_chain_function (pad, GST_DEBUG_FUNCPTR (gst_collect_pads_chain)); - gst_pad_set_event_function (pad, GST_DEBUG_FUNCPTR (gst_collect_pads_event)); - gst_pad_set_query_function (pad, GST_DEBUG_FUNCPTR (gst_collect_pads_query)); - /* backward compat, also add to data if stopped, so that the element already - * has this in the public data list before going PAUSED (typically) - * this can only be done when we are stopped because we don't take the - * STREAM_LOCK to protect the pads->data list. */ - if (!pads->priv->started) { - pads->data = g_slist_append (pads->data, data); - ref_data (data); - } - /* activate the pad when needed */ - if (pads->priv->started) - gst_pad_set_active (pad, TRUE); - pads->priv->pad_cookie++; - GST_OBJECT_UNLOCK (pads); - - return data; -} - -static gint -find_pad (GstCollectData * data, GstPad * pad) -{ - if (data->pad == pad) - return 0; - return 1; -} - -/** - * gst_collect_pads_remove_pad: - * @pads: the collectpads to use - * @pad: (transfer none): the pad to remove - * - * Remove a pad from the collection of collect pads. This function will also - * free the #GstCollectData and all the resources that were allocated with - * gst_collect_pads_add_pad(). - * - * The pad will be deactivated automatically when @pads is stopped. - * - * MT safe. - * - * Returns: %TRUE if the pad could be removed. - */ -gboolean -gst_collect_pads_remove_pad (GstCollectPads * pads, GstPad * pad) -{ - GstCollectData *data; - GSList *list; - - g_return_val_if_fail (pads != NULL, FALSE); - g_return_val_if_fail (GST_IS_COLLECT_PADS (pads), FALSE); - g_return_val_if_fail (pad != NULL, FALSE); - g_return_val_if_fail (GST_IS_PAD (pad), FALSE); - - GST_DEBUG_OBJECT (pads, "removing pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - - GST_OBJECT_LOCK (pads); - list = - g_slist_find_custom (pads->priv->pad_list, pad, (GCompareFunc) find_pad); - if (!list) - goto unknown_pad; - - data = (GstCollectData *) list->data; - - GST_DEBUG_OBJECT (pads, "found pad %s:%s at %p", GST_DEBUG_PAD_NAME (pad), - data); - - /* clear the stuff we configured */ - gst_pad_set_chain_function (pad, NULL); - gst_pad_set_event_function (pad, NULL); - GST_OBJECT_LOCK (pad); - gst_pad_set_element_private (pad, NULL); - GST_OBJECT_UNLOCK (pad); - - /* backward compat, also remove from data if stopped, note that this function - * can only be called when we are stopped because we don't take the - * STREAM_LOCK to protect the pads->data list. */ - if (!pads->priv->started) { - GSList *dlist; - - dlist = g_slist_find_custom (pads->data, pad, (GCompareFunc) find_pad); - if (dlist) { - GstCollectData *pdata = dlist->data; - - pads->data = g_slist_delete_link (pads->data, dlist); - unref_data (pdata); - } - } - /* remove from the pad list */ - pads->priv->pad_list = g_slist_delete_link (pads->priv->pad_list, list); - pads->priv->pad_cookie++; - - /* signal waiters because something changed */ - GST_COLLECT_PADS_EVT_BROADCAST (pads); - - /* deactivate the pad when needed */ - if (!pads->priv->started) - gst_pad_set_active (pad, FALSE); - - /* clean and free the collect data */ - unref_data (data); - - GST_OBJECT_UNLOCK (pads); - - return TRUE; - -unknown_pad: - { - GST_WARNING_OBJECT (pads, "cannot remove unknown pad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - GST_OBJECT_UNLOCK (pads); - return FALSE; - } -} - -/* - * Must be called with STREAM_LOCK and OBJECT_LOCK. - */ -static void -gst_collect_pads_set_flushing_unlocked (GstCollectPads * pads, - gboolean flushing) -{ - GSList *walk = NULL; - - GST_DEBUG ("sink-pads flushing=%d", flushing); - - /* Update the pads flushing flag */ - for (walk = pads->priv->pad_list; walk; walk = g_slist_next (walk)) { - GstCollectData *cdata = walk->data; - - if (GST_IS_PAD (cdata->pad)) { - GST_OBJECT_LOCK (cdata->pad); - if (flushing) - GST_PAD_SET_FLUSHING (cdata->pad); - else - GST_PAD_UNSET_FLUSHING (cdata->pad); - if (flushing) - GST_COLLECT_PADS_STATE_SET (cdata, GST_COLLECT_PADS_STATE_FLUSHING); - else - GST_COLLECT_PADS_STATE_UNSET (cdata, GST_COLLECT_PADS_STATE_FLUSHING); - gst_collect_pads_clear (pads, cdata); - GST_OBJECT_UNLOCK (cdata->pad); - } - } - - /* inform _chain of changes */ - GST_COLLECT_PADS_EVT_BROADCAST (pads); -} - -/** - * gst_collect_pads_set_flushing: - * @pads: the collectpads to use - * @flushing: desired state of the pads - * - * Change the flushing state of all the pads in the collection. No pad - * is able to accept anymore data when @flushing is %TRUE. Calling this - * function with @flushing %FALSE makes @pads accept data again. - * Caller must ensure that downstream streaming (thread) is not blocked, - * e.g. by sending a FLUSH_START downstream. - * - * MT safe. - */ -void -gst_collect_pads_set_flushing (GstCollectPads * pads, gboolean flushing) -{ - g_return_if_fail (pads != NULL); - g_return_if_fail (GST_IS_COLLECT_PADS (pads)); - - /* NOTE since this eventually calls _pop, some (STREAM_)LOCK is needed here */ - GST_COLLECT_PADS_STREAM_LOCK (pads); - GST_OBJECT_LOCK (pads); - gst_collect_pads_set_flushing_unlocked (pads, flushing); - GST_OBJECT_UNLOCK (pads); - GST_COLLECT_PADS_STREAM_UNLOCK (pads); -} - -/** - * gst_collect_pads_start: - * @pads: the collectpads to use - * - * Starts the processing of data in the collect_pads. - * - * MT safe. - */ -void -gst_collect_pads_start (GstCollectPads * pads) -{ - GSList *collected; - - g_return_if_fail (pads != NULL); - g_return_if_fail (GST_IS_COLLECT_PADS (pads)); - - GST_DEBUG_OBJECT (pads, "starting collect pads"); - - /* make sure stop and collect cannot be called anymore */ - GST_COLLECT_PADS_STREAM_LOCK (pads); - - /* make pads streamable */ - GST_OBJECT_LOCK (pads); - - /* loop over the master pad list and reset the segment */ - collected = pads->priv->pad_list; - for (; collected; collected = g_slist_next (collected)) { - GstCollectData *data; - - data = collected->data; - gst_segment_init (&data->segment, GST_FORMAT_UNDEFINED); - } - - gst_collect_pads_set_flushing_unlocked (pads, FALSE); - - /* Start collect pads */ - pads->priv->started = TRUE; - GST_OBJECT_UNLOCK (pads); - GST_COLLECT_PADS_STREAM_UNLOCK (pads); -} - -/** - * gst_collect_pads_stop: - * @pads: the collectpads to use - * - * Stops the processing of data in the collect_pads. this function - * will also unblock any blocking operations. - * - * MT safe. - */ -void -gst_collect_pads_stop (GstCollectPads * pads) -{ - GSList *collected; - - g_return_if_fail (pads != NULL); - g_return_if_fail (GST_IS_COLLECT_PADS (pads)); - - GST_DEBUG_OBJECT (pads, "stopping collect pads"); - - /* make sure collect and start cannot be called anymore */ - GST_COLLECT_PADS_STREAM_LOCK (pads); - - /* make pads not accept data anymore */ - GST_OBJECT_LOCK (pads); - gst_collect_pads_set_flushing_unlocked (pads, TRUE); - - /* Stop collect pads */ - pads->priv->started = FALSE; - pads->priv->eospads = 0; - pads->priv->queuedpads = 0; - - /* loop over the master pad list and flush buffers */ - collected = pads->priv->pad_list; - for (; collected; collected = g_slist_next (collected)) { - GstCollectData *data; - GstBuffer **buffer_p; - - data = collected->data; - if (data->buffer) { - buffer_p = &data->buffer; - gst_buffer_replace (buffer_p, NULL); - data->pos = 0; - } - GST_COLLECT_PADS_STATE_UNSET (data, GST_COLLECT_PADS_STATE_EOS); - } - - if (pads->priv->earliest_data) - unref_data (pads->priv->earliest_data); - pads->priv->earliest_data = NULL; - pads->priv->earliest_time = GST_CLOCK_TIME_NONE; - - GST_OBJECT_UNLOCK (pads); - /* Wake them up so they can end the chain functions. */ - GST_COLLECT_PADS_EVT_BROADCAST (pads); - - GST_COLLECT_PADS_STREAM_UNLOCK (pads); -} - -/** - * gst_collect_pads_peek: - * @pads: the collectpads to peek - * @data: the data to use - * - * Peek at the buffer currently queued in @data. This function - * should be called with the @pads STREAM_LOCK held, such as in the callback - * handler. - * - * MT safe. - * - * Returns: (transfer full) (nullable): The buffer in @data or %NULL if no - * buffer is queued. should unref the buffer after usage. - */ -GstBuffer * -gst_collect_pads_peek (GstCollectPads * pads, GstCollectData * data) -{ - GstBuffer *result; - - g_return_val_if_fail (pads != NULL, NULL); - g_return_val_if_fail (GST_IS_COLLECT_PADS (pads), NULL); - g_return_val_if_fail (data != NULL, NULL); - - if ((result = data->buffer)) - gst_buffer_ref (result); - - GST_DEBUG_OBJECT (pads, "Peeking at pad %s:%s: buffer=%" GST_PTR_FORMAT, - GST_DEBUG_PAD_NAME (data->pad), result); - - return result; -} - -/** - * gst_collect_pads_pop: - * @pads: the collectpads to pop - * @data: the data to use - * - * Pop the buffer currently queued in @data. This function - * should be called with the @pads STREAM_LOCK held, such as in the callback - * handler. - * - * MT safe. - * - * Returns: (transfer full) (nullable): The buffer in @data or %NULL if no - * buffer was queued. You should unref the buffer after usage. - */ -GstBuffer * -gst_collect_pads_pop (GstCollectPads * pads, GstCollectData * data) -{ - GstBuffer *result; - - g_return_val_if_fail (pads != NULL, NULL); - g_return_val_if_fail (GST_IS_COLLECT_PADS (pads), NULL); - g_return_val_if_fail (data != NULL, NULL); - - if ((result = data->buffer)) { - data->buffer = NULL; - data->pos = 0; - /* one less pad with queued data now */ - if (GST_COLLECT_PADS_STATE_IS_SET (data, GST_COLLECT_PADS_STATE_WAITING)) - pads->priv->queuedpads--; - } - - GST_COLLECT_PADS_EVT_BROADCAST (pads); - - GST_DEBUG_OBJECT (pads, "Pop buffer on pad %s:%s: buffer=%" GST_PTR_FORMAT, - GST_DEBUG_PAD_NAME (data->pad), result); - - return result; -} - -/* pop and unref the currently queued buffer, should be called with STREAM_LOCK - * held */ -static void -gst_collect_pads_clear (GstCollectPads * pads, GstCollectData * data) -{ - GstBuffer *buf; - - if ((buf = gst_collect_pads_pop (pads, data))) - gst_buffer_unref (buf); -} - -/** - * gst_collect_pads_available: - * @pads: the collectpads to query - * - * Query how much bytes can be read from each queued buffer. This means - * that the result of this call is the maximum number of bytes that can - * be read from each of the pads. - * - * This function should be called with @pads STREAM_LOCK held, such as - * in the callback. - * - * MT safe. - * - * Returns: The maximum number of bytes queued on all pads. This function - * returns 0 if a pad has no queued buffer. - */ -/* we might pre-calculate this in some struct field, - * but would then have to maintain this in _chain and particularly _pop, etc, - * even if element is never interested in this information */ -guint -gst_collect_pads_available (GstCollectPads * pads) -{ - GSList *collected; - guint result = G_MAXUINT; - - g_return_val_if_fail (pads != NULL, 0); - g_return_val_if_fail (GST_IS_COLLECT_PADS (pads), 0); - - collected = pads->data; - for (; collected; collected = g_slist_next (collected)) { - GstCollectData *pdata; - GstBuffer *buffer; - gint size; - - pdata = (GstCollectData *) collected->data; - - /* ignore pad with EOS */ - if (G_UNLIKELY (GST_COLLECT_PADS_STATE_IS_SET (pdata, - GST_COLLECT_PADS_STATE_EOS))) { - GST_DEBUG_OBJECT (pads, "pad %p is EOS", pdata); - continue; - } - - /* an empty buffer without EOS is weird when we get here.. */ - if (G_UNLIKELY ((buffer = pdata->buffer) == NULL)) { - GST_WARNING_OBJECT (pads, "pad %p has no buffer", pdata); - goto not_filled; - } - - /* this is the size left of the buffer */ - size = gst_buffer_get_size (buffer) - pdata->pos; - GST_DEBUG_OBJECT (pads, "pad %p has %d bytes left", pdata, size); - - /* need to return the min of all available data */ - if (size < result) - result = size; - } - /* nothing changed, all must be EOS then, return 0 */ - if (G_UNLIKELY (result == G_MAXUINT)) - result = 0; - - return result; - -not_filled: - { - return 0; - } -} - -/** - * gst_collect_pads_flush: - * @pads: the collectpads to query - * @data: the data to use - * @size: the number of bytes to flush - * - * Flush @size bytes from the pad @data. - * - * This function should be called with @pads STREAM_LOCK held, such as - * in the callback. - * - * MT safe. - * - * Returns: The number of bytes flushed This can be less than @size and - * is 0 if the pad was end-of-stream. - */ -guint -gst_collect_pads_flush (GstCollectPads * pads, GstCollectData * data, - guint size) -{ - guint flushsize; - gsize bsize; - GstBuffer *buffer; - - g_return_val_if_fail (pads != NULL, 0); - g_return_val_if_fail (GST_IS_COLLECT_PADS (pads), 0); - g_return_val_if_fail (data != NULL, 0); - - /* no buffer, must be EOS */ - if ((buffer = data->buffer) == NULL) - return 0; - - bsize = gst_buffer_get_size (buffer); - - /* this is what we can flush at max */ - flushsize = MIN (size, bsize - data->pos); - - data->pos += size; - - if (data->pos >= bsize) - /* _clear will also reset data->pos to 0 */ - gst_collect_pads_clear (pads, data); - - return flushsize; -} - -/** - * gst_collect_pads_read_buffer: - * @pads: the collectpads to query - * @data: the data to use - * @size: the number of bytes to read - * - * Get a subbuffer of @size bytes from the given pad @data. - * - * This function should be called with @pads STREAM_LOCK held, such as in the - * callback. - * - * MT safe. - * - * Returns: (transfer full) (nullable): A sub buffer. The size of the buffer can - * be less that requested. A return of %NULL signals that the pad is - * end-of-stream. Unref the buffer after use. - */ -GstBuffer * -gst_collect_pads_read_buffer (GstCollectPads * pads, GstCollectData * data, - guint size) -{ - guint readsize, buf_size; - GstBuffer *buffer; - - g_return_val_if_fail (pads != NULL, NULL); - g_return_val_if_fail (GST_IS_COLLECT_PADS (pads), NULL); - g_return_val_if_fail (data != NULL, NULL); - - /* no buffer, must be EOS */ - if ((buffer = data->buffer) == NULL) - return NULL; - - buf_size = gst_buffer_get_size (buffer); - readsize = MIN (size, buf_size - data->pos); - - return gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, data->pos, - readsize); -} - -/** - * gst_collect_pads_take_buffer: - * @pads: the collectpads to query - * @data: the data to use - * @size: the number of bytes to read - * - * Get a subbuffer of @size bytes from the given pad @data. Flushes the amount - * of read bytes. - * - * This function should be called with @pads STREAM_LOCK held, such as in the - * callback. - * - * MT safe. - * - * Returns: (transfer full) (nullable): A sub buffer. The size of the buffer can - * be less that requested. A return of %NULL signals that the pad is - * end-of-stream. Unref the buffer after use. - */ -GstBuffer * -gst_collect_pads_take_buffer (GstCollectPads * pads, GstCollectData * data, - guint size) -{ - GstBuffer *buffer = gst_collect_pads_read_buffer (pads, data, size); - - if (buffer) { - gst_collect_pads_flush (pads, data, gst_buffer_get_size (buffer)); - } - return buffer; -} - -/** - * gst_collect_pads_set_waiting: - * @pads: the collectpads - * @data: the data to use - * @waiting: boolean indicating whether this pad should operate - * in waiting or non-waiting mode - * - * Sets a pad to waiting or non-waiting mode, if at least this pad - * has not been created with locked waiting state, - * in which case nothing happens. - * - * This function should be called with @pads STREAM_LOCK held, such as - * in the callback. - * - * MT safe. - */ -void -gst_collect_pads_set_waiting (GstCollectPads * pads, GstCollectData * data, - gboolean waiting) -{ - g_return_if_fail (pads != NULL); - g_return_if_fail (GST_IS_COLLECT_PADS (pads)); - g_return_if_fail (data != NULL); - - GST_DEBUG_OBJECT (pads, "Setting pad %s to waiting %d, locked %d", - GST_PAD_NAME (data->pad), waiting, - GST_COLLECT_PADS_STATE_IS_SET (data, GST_COLLECT_PADS_STATE_LOCKED)); - - /* Do something only on a change and if not locked */ - if (!GST_COLLECT_PADS_STATE_IS_SET (data, GST_COLLECT_PADS_STATE_LOCKED) && - (GST_COLLECT_PADS_STATE_IS_SET (data, GST_COLLECT_PADS_STATE_WAITING) != - ! !waiting)) { - /* Set waiting state for this pad */ - if (waiting) - GST_COLLECT_PADS_STATE_SET (data, GST_COLLECT_PADS_STATE_WAITING); - else - GST_COLLECT_PADS_STATE_UNSET (data, GST_COLLECT_PADS_STATE_WAITING); - /* Update number of queued pads if needed */ - if (!data->buffer && - !GST_COLLECT_PADS_STATE_IS_SET (data, GST_COLLECT_PADS_STATE_EOS)) { - if (waiting) - pads->priv->queuedpads--; - else - pads->priv->queuedpads++; - } - - /* signal waiters because something changed */ - GST_COLLECT_PADS_EVT_BROADCAST (pads); - } -} - -/* see if pads were added or removed and update our stats. Any pad - * added after releasing the LOCK will get collected in the next - * round. - * - * We can do a quick check by checking the cookies, that get changed - * whenever the pad list is updated. - * - * Must be called with STREAM_LOCK. - */ -static void -gst_collect_pads_check_pads (GstCollectPads * pads) -{ - /* the master list and cookie are protected with LOCK */ - GST_OBJECT_LOCK (pads); - if (G_UNLIKELY (pads->priv->pad_cookie != pads->priv->cookie)) { - GSList *collected; - - /* clear list and stats */ - g_slist_foreach (pads->data, (GFunc) unref_data, NULL); - g_slist_free (pads->data); - pads->data = NULL; - pads->priv->numpads = 0; - pads->priv->queuedpads = 0; - pads->priv->eospads = 0; - if (pads->priv->earliest_data) - unref_data (pads->priv->earliest_data); - pads->priv->earliest_data = NULL; - pads->priv->earliest_time = GST_CLOCK_TIME_NONE; - - /* loop over the master pad list */ - collected = pads->priv->pad_list; - for (; collected; collected = g_slist_next (collected)) { - GstCollectData *data; - - /* update the stats */ - pads->priv->numpads++; - data = collected->data; - if (GST_COLLECT_PADS_STATE_IS_SET (data, GST_COLLECT_PADS_STATE_EOS)) - pads->priv->eospads++; - else if (data->buffer || !GST_COLLECT_PADS_STATE_IS_SET (data, - GST_COLLECT_PADS_STATE_WAITING)) - pads->priv->queuedpads++; - - /* add to the list of pads to collect */ - ref_data (data); - /* preserve order of adding/requesting pads */ - pads->data = g_slist_append (pads->data, data); - } - /* and update the cookie */ - pads->priv->cookie = pads->priv->pad_cookie; - } - GST_OBJECT_UNLOCK (pads); -} - -/* checks if all the pads are collected and call the collectfunction - * - * Should be called with STREAM_LOCK. - * - * Returns: The #GstFlowReturn of collection. - */ -static GstFlowReturn -gst_collect_pads_check_collected (GstCollectPads * pads) -{ - GstFlowReturn flow_ret = GST_FLOW_OK; - GstCollectPadsFunction func; - gpointer user_data; - - g_return_val_if_fail (GST_IS_COLLECT_PADS (pads), GST_FLOW_ERROR); - - GST_OBJECT_LOCK (pads); - func = pads->priv->func; - user_data = pads->priv->user_data; - GST_OBJECT_UNLOCK (pads); - - g_return_val_if_fail (pads->priv->func != NULL, GST_FLOW_NOT_SUPPORTED); - - /* check for new pads, update stats etc.. */ - gst_collect_pads_check_pads (pads); - - if (G_UNLIKELY (pads->priv->eospads == pads->priv->numpads)) { - /* If all our pads are EOS just collect once to let the element - * do its final EOS handling. */ - GST_DEBUG_OBJECT (pads, "All active pads (%d) are EOS, calling %s", - pads->priv->numpads, GST_DEBUG_FUNCPTR_NAME (func)); - - if (G_UNLIKELY (g_atomic_int_compare_and_exchange (&pads->priv->seeking, - TRUE, FALSE))) { - GST_INFO_OBJECT (pads, "finished seeking"); - } - do { - flow_ret = func (pads, user_data); - } while (flow_ret == GST_FLOW_OK); - } else { - gboolean collected = FALSE; - - /* We call the collected function as long as our condition matches. */ - while (((pads->priv->queuedpads + pads->priv->eospads) >= - pads->priv->numpads)) { - GST_DEBUG_OBJECT (pads, - "All active pads (%d + %d >= %d) have data, " "calling %s", - pads->priv->queuedpads, pads->priv->eospads, pads->priv->numpads, - GST_DEBUG_FUNCPTR_NAME (func)); - - if (G_UNLIKELY (g_atomic_int_compare_and_exchange (&pads->priv->seeking, - TRUE, FALSE))) { - GST_INFO_OBJECT (pads, "finished seeking"); - } - flow_ret = func (pads, user_data); - collected = TRUE; - - /* break on error */ - if (flow_ret != GST_FLOW_OK) - break; - /* Don't keep looping after telling the element EOS or flushing */ - if (pads->priv->queuedpads == 0) - break; - } - if (!collected) - GST_DEBUG_OBJECT (pads, "Not all active pads (%d) have data, continuing", - pads->priv->numpads); - } - return flow_ret; -} - - -/* General overview: - * - only pad with a buffer can determine earliest_data (and earliest_time) - * - only segment info determines (non-)waiting state - * - ? perhaps use _stream_time for comparison - * (which muxers might have use as well ?) - */ - -/* - * Function to recalculate the waiting state of all pads. - * - * Must be called with STREAM_LOCK. - * - * Returns %TRUE if a pad was set to waiting - * (from non-waiting state). - */ -static gboolean -gst_collect_pads_recalculate_waiting (GstCollectPads * pads) -{ - GSList *collected; - gboolean result = FALSE; - - /* If earliest time is not known, there is nothing to do. */ - if (pads->priv->earliest_data == NULL) - return FALSE; - - for (collected = pads->data; collected; collected = g_slist_next (collected)) { - GstCollectData *data = (GstCollectData *) collected->data; - int cmp_res; - GstClockTime comp_time; - - /* check if pad has a segment */ - if (data->segment.format == GST_FORMAT_UNDEFINED) { - GST_WARNING_OBJECT (pads, - "GstCollectPads has no time segment, assuming 0 based."); - gst_segment_init (&data->segment, GST_FORMAT_TIME); - GST_COLLECT_PADS_STATE_SET (data, GST_COLLECT_PADS_STATE_NEW_SEGMENT); - } - - /* check segment format */ - if (data->segment.format != GST_FORMAT_TIME) { - GST_ERROR_OBJECT (pads, "GstCollectPads can handle only time segments."); - continue; - } - - /* check if the waiting state should be changed */ - comp_time = data->segment.position; - cmp_res = pads->priv->compare_func (pads, data, comp_time, - pads->priv->earliest_data, pads->priv->earliest_time, - pads->priv->compare_user_data); - if (cmp_res > 0) - /* stop waiting */ - gst_collect_pads_set_waiting (pads, data, FALSE); - else { - if (!GST_COLLECT_PADS_STATE_IS_SET (data, GST_COLLECT_PADS_STATE_WAITING)) { - /* start waiting */ - gst_collect_pads_set_waiting (pads, data, TRUE); - result = TRUE; - } - } - } - - return result; -} - -/** - * gst_collect_pads_find_best_pad: - * @pads: the collectpads to use - * @data: returns the collectdata for earliest data - * @time: returns the earliest available buffertime - * - * Find the oldest/best pad, i.e. pad holding the oldest buffer and - * and return the corresponding #GstCollectData and buffertime. - * - * This function should be called with STREAM_LOCK held, - * such as in the callback. - */ -static void -gst_collect_pads_find_best_pad (GstCollectPads * pads, - GstCollectData ** data, GstClockTime * time) -{ - GSList *collected; - GstCollectData *best = NULL; - GstClockTime best_time = GST_CLOCK_TIME_NONE; - - g_return_if_fail (data != NULL); - g_return_if_fail (time != NULL); - - for (collected = pads->data; collected; collected = g_slist_next (collected)) { - GstBuffer *buffer; - GstCollectData *data = (GstCollectData *) collected->data; - GstClockTime timestamp; - - buffer = gst_collect_pads_peek (pads, data); - /* if we have a buffer check if it is better then the current best one */ - if (buffer != NULL) { - timestamp = GST_BUFFER_DTS_OR_PTS (buffer); - gst_buffer_unref (buffer); - if (best == NULL || pads->priv->compare_func (pads, data, timestamp, - best, best_time, pads->priv->compare_user_data) < 0) { - best = data; - best_time = timestamp; - } - } - } - - /* set earliest time */ - *data = best; - *time = best_time; - - GST_DEBUG_OBJECT (pads, "best pad %s, best time %" GST_TIME_FORMAT, - best ? GST_PAD_NAME (((GstCollectData *) best)->pad) : "(nil)", - GST_TIME_ARGS (best_time)); -} - -/* - * Function to recalculate earliest_data and earliest_timestamp. This also calls - * gst_collect_pads_recalculate_waiting - * - * Must be called with STREAM_LOCK. - */ -static gboolean -gst_collect_pads_recalculate_full (GstCollectPads * pads) -{ - if (pads->priv->earliest_data) - unref_data (pads->priv->earliest_data); - gst_collect_pads_find_best_pad (pads, &pads->priv->earliest_data, - &pads->priv->earliest_time); - if (pads->priv->earliest_data) - ref_data (pads->priv->earliest_data); - return gst_collect_pads_recalculate_waiting (pads); -} - -/* - * Default collect callback triggered when #GstCollectPads gathered all data. - * - * Called with STREAM_LOCK. - */ -static GstFlowReturn -gst_collect_pads_default_collected (GstCollectPads * pads, gpointer user_data) -{ - GstCollectData *best = NULL; - GstBuffer *buffer; - GstFlowReturn ret = GST_FLOW_OK; - GstCollectPadsBufferFunction func; - gpointer buffer_user_data; - - g_return_val_if_fail (GST_IS_COLLECT_PADS (pads), GST_FLOW_ERROR); - - GST_OBJECT_LOCK (pads); - func = pads->priv->buffer_func; - buffer_user_data = pads->priv->buffer_user_data; - GST_OBJECT_UNLOCK (pads); - - g_return_val_if_fail (func != NULL, GST_FLOW_NOT_SUPPORTED); - - /* Find the oldest pad at all cost */ - if (gst_collect_pads_recalculate_full (pads)) { - /* waiting was switched on, - * so give another thread a chance to deliver a possibly - * older buffer; don't charge on yet with the current oldest */ - ret = GST_FLOW_OK; - goto done; - } - - best = pads->priv->earliest_data; - - /* No data collected means EOS. */ - if (G_UNLIKELY (best == NULL)) { - ret = func (pads, best, NULL, buffer_user_data); - if (ret == GST_FLOW_OK) - ret = GST_FLOW_EOS; - goto done; - } - - /* make sure that the pad we take a buffer from is waiting; - * otherwise popping a buffer will seem not to have happened - * and collectpads can get into a busy loop */ - gst_collect_pads_set_waiting (pads, best, TRUE); - - /* Send buffer */ - buffer = gst_collect_pads_pop (pads, best); - ret = func (pads, best, buffer, buffer_user_data); - - /* maybe non-waiting was forced to waiting above due to - * newsegment events coming too sparsely, - * so re-check to restore state to avoid hanging/waiting */ - gst_collect_pads_recalculate_full (pads); - -done: - return ret; -} - -/* - * Default timestamp compare function. - */ -static gint -gst_collect_pads_default_compare_func (GstCollectPads * pads, - GstCollectData * data1, GstClockTime timestamp1, - GstCollectData * data2, GstClockTime timestamp2, gpointer user_data) -{ - - GST_LOG_OBJECT (pads, "comparing %" GST_TIME_FORMAT - " and %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp1), - GST_TIME_ARGS (timestamp2)); - /* non-valid timestamps go first as they are probably headers or so */ - if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp1))) - return GST_CLOCK_TIME_IS_VALID (timestamp2) ? -1 : 0; - - if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp2))) - return 1; - - /* compare timestamp */ - if (timestamp1 < timestamp2) - return -1; - - if (timestamp1 > timestamp2) - return 1; - - return 0; -} - -/* called with STREAM_LOCK */ -static void -gst_collect_pads_handle_position_update (GstCollectPads * pads, - GstCollectData * data, GstClockTime new_pos) -{ - gint cmp_res; - - /* If oldest time is not known, or current pad got newsegment; - * recalculate the state */ - if (!pads->priv->earliest_data || pads->priv->earliest_data == data) { - gst_collect_pads_recalculate_full (pads); - goto exit; - } - - /* Check if the waiting state of the pad should change. */ - cmp_res = - pads->priv->compare_func (pads, data, new_pos, - pads->priv->earliest_data, pads->priv->earliest_time, - pads->priv->compare_user_data); - - if (cmp_res > 0) - /* Stop waiting */ - gst_collect_pads_set_waiting (pads, data, FALSE); - -exit: - return; - -} - -static GstClockTime -gst_collect_pads_clip_time (GstCollectPads * pads, GstCollectData * data, - GstClockTime time) -{ - GstClockTime otime = time; - GstBuffer *in, *out = NULL; - - if (pads->priv->clip_func) { - in = gst_buffer_new (); - GST_BUFFER_PTS (in) = time; - GST_BUFFER_DTS (in) = GST_CLOCK_TIME_NONE; - pads->priv->clip_func (pads, data, in, &out, pads->priv->clip_user_data); - if (out) { - otime = GST_BUFFER_PTS (out); - gst_buffer_unref (out); - } else { - /* FIXME should distinguish between ahead or after segment, - * let's assume after segment and use some large time ... */ - otime = G_MAXINT64 / 2; - } - } - - return otime; -} - -/** - * gst_collect_pads_event_default: - * @pads: the collectpads to use - * @data: collect data of corresponding pad - * @event: event being processed - * @discard: process but do not send event downstream - * - * Default #GstCollectPads event handling that elements should always - * chain up to to ensure proper operation. Element might however indicate - * event should not be forwarded downstream. - */ -gboolean -gst_collect_pads_event_default (GstCollectPads * pads, GstCollectData * data, - GstEvent * event, gboolean discard) -{ - gboolean res = TRUE; - GstCollectPadsBufferFunction buffer_func; - GstObject *parent; - GstPad *pad; - - GST_OBJECT_LOCK (pads); - buffer_func = pads->priv->buffer_func; - GST_OBJECT_UNLOCK (pads); - - pad = data->pad; - parent = GST_OBJECT_PARENT (pad); - - GST_DEBUG_OBJECT (pad, "Got '%s' event", GST_EVENT_TYPE_NAME (event)); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_START: - { - if (g_atomic_int_get (&pads->priv->seeking)) { - /* drop all but the first FLUSH_STARTs when seeking */ - if (!g_atomic_int_compare_and_exchange (&pads-> - priv->pending_flush_start, TRUE, FALSE)) - goto eat; - - /* unblock collect pads */ - gst_pad_event_default (pad, parent, event); - event = NULL; - - GST_COLLECT_PADS_STREAM_LOCK (pads); - /* Start flushing. We never call gst_collect_pads_set_flushing (FALSE), we - * instead wait until each pad gets its FLUSH_STOP and let that reset the pad to - * non-flushing (which happens in gst_collect_pads_event_default). - */ - gst_collect_pads_set_flushing (pads, TRUE); - - if (pads->priv->flush_func) - pads->priv->flush_func (pads, pads->priv->flush_user_data); - - g_atomic_int_set (&pads->priv->pending_flush_stop, TRUE); - GST_COLLECT_PADS_STREAM_UNLOCK (pads); - - goto eat; - } else { - /* forward event to unblock check_collected */ - GST_DEBUG_OBJECT (pad, "forwarding flush start"); - if (!(res = gst_pad_event_default (pad, parent, event))) { - GST_WARNING_OBJECT (pad, "forwarding flush start failed"); - } - event = NULL; - - /* now unblock the chain function. - * no cond per pad, so they all unblock, - * non-flushing block again */ - GST_COLLECT_PADS_STREAM_LOCK (pads); - GST_COLLECT_PADS_STATE_SET (data, GST_COLLECT_PADS_STATE_FLUSHING); - gst_collect_pads_clear (pads, data); - - /* cater for possible default muxing functionality */ - if (buffer_func) { - /* restore to initial state */ - gst_collect_pads_set_waiting (pads, data, TRUE); - /* if the current pad is affected, reset state, recalculate later */ - if (pads->priv->earliest_data == data) { - unref_data (data); - pads->priv->earliest_data = NULL; - pads->priv->earliest_time = GST_CLOCK_TIME_NONE; - } - } - - GST_COLLECT_PADS_STREAM_UNLOCK (pads); - - goto eat; - } - } - case GST_EVENT_FLUSH_STOP: - { - /* flush the 1 buffer queue */ - GST_COLLECT_PADS_STREAM_LOCK (pads); - GST_COLLECT_PADS_STATE_UNSET (data, GST_COLLECT_PADS_STATE_FLUSHING); - gst_collect_pads_clear (pads, data); - /* we need new segment info after the flush */ - gst_segment_init (&data->segment, GST_FORMAT_UNDEFINED); - GST_COLLECT_PADS_STATE_UNSET (data, GST_COLLECT_PADS_STATE_NEW_SEGMENT); - /* if the pad was EOS, remove the EOS flag and - * decrement the number of eospads */ - if (G_UNLIKELY (GST_COLLECT_PADS_STATE_IS_SET (data, - GST_COLLECT_PADS_STATE_EOS))) { - if (!GST_COLLECT_PADS_STATE_IS_SET (data, - GST_COLLECT_PADS_STATE_WAITING)) - pads->priv->queuedpads++; - if (!g_atomic_int_get (&pads->priv->seeking)) { - pads->priv->eospads--; - } - GST_COLLECT_PADS_STATE_UNSET (data, GST_COLLECT_PADS_STATE_EOS); - } - GST_COLLECT_PADS_STREAM_UNLOCK (pads); - - if (g_atomic_int_get (&pads->priv->seeking)) { - if (g_atomic_int_compare_and_exchange (&pads->priv->pending_flush_stop, - TRUE, FALSE)) - goto forward; - else - goto eat; - } else { - goto forward; - } - } - case GST_EVENT_EOS: - { - GST_COLLECT_PADS_STREAM_LOCK (pads); - /* if the pad was not EOS, make it EOS and so we - * have one more eospad */ - if (G_LIKELY (!GST_COLLECT_PADS_STATE_IS_SET (data, - GST_COLLECT_PADS_STATE_EOS))) { - GST_COLLECT_PADS_STATE_SET (data, GST_COLLECT_PADS_STATE_EOS); - if (!GST_COLLECT_PADS_STATE_IS_SET (data, - GST_COLLECT_PADS_STATE_WAITING)) - pads->priv->queuedpads--; - pads->priv->eospads++; - } - /* check if we need collecting anything, we ignore the result. */ - gst_collect_pads_check_collected (pads); - GST_COLLECT_PADS_STREAM_UNLOCK (pads); - - goto eat; - } - case GST_EVENT_SEGMENT: - { - GstSegment seg; - - GST_COLLECT_PADS_STREAM_LOCK (pads); - - gst_event_copy_segment (event, &seg); - - GST_DEBUG_OBJECT (data->pad, "got segment %" GST_SEGMENT_FORMAT, &seg); - - /* default collection can not handle other segment formats than time */ - if (buffer_func && seg.format != GST_FORMAT_TIME) { - GST_WARNING_OBJECT (pads, "GstCollectPads default collecting " - "can only handle time segments. Non time segment ignored."); - goto newsegment_done; - } - - /* need to update segment first */ - data->segment = seg; - GST_COLLECT_PADS_STATE_SET (data, GST_COLLECT_PADS_STATE_NEW_SEGMENT); - - /* now we can use for e.g. running time */ - seg.position = - gst_collect_pads_clip_time (pads, data, seg.start + seg.offset); - /* update again */ - data->segment = seg; - - /* default muxing functionality */ - if (!buffer_func) - goto newsegment_done; - - gst_collect_pads_handle_position_update (pads, data, seg.position); - - newsegment_done: - GST_COLLECT_PADS_STREAM_UNLOCK (pads); - /* we must not forward this event since multiple segments will be - * accumulated and this is certainly not what we want. */ - goto eat; - } - case GST_EVENT_GAP: - { - GstClockTime start, duration; - - GST_COLLECT_PADS_STREAM_LOCK (pads); - - gst_event_parse_gap (event, &start, &duration); - /* FIXME, handle reverse playback case */ - if (GST_CLOCK_TIME_IS_VALID (duration)) - start += duration; - /* we do not expect another buffer until after gap, - * so that is our position now */ - data->segment.position = gst_collect_pads_clip_time (pads, data, start); - - gst_collect_pads_handle_position_update (pads, data, - data->segment.position); - - GST_COLLECT_PADS_STREAM_UNLOCK (pads); - goto eat; - } - case GST_EVENT_STREAM_START: - /* drop stream start events, element must create its own start event, - * we can't just forward the first random stream start event we get */ - goto eat; - case GST_EVENT_CAPS: - goto eat; - default: - /* forward other events */ - goto forward; - } - -eat: - GST_DEBUG_OBJECT (pads, "dropping event: %" GST_PTR_FORMAT, event); - if (event) - gst_event_unref (event); - return res; - -forward: - if (discard) - goto eat; - else { - GST_DEBUG_OBJECT (pads, "forward event: %" GST_PTR_FORMAT, event); - return gst_pad_event_default (pad, parent, event); - } -} - -typedef struct -{ - GstEvent *event; - gboolean result; -} EventData; - -static gboolean -event_forward_func (GstPad * pad, EventData * data) -{ - gboolean ret = TRUE; - GstPad *peer = gst_pad_get_peer (pad); - - if (peer) { - ret = gst_pad_send_event (peer, gst_event_ref (data->event)); - gst_object_unref (peer); - } - - data->result &= ret; - /* Always send to all pads */ - return FALSE; -} - -static gboolean -forward_event_to_all_sinkpads (GstPad * srcpad, GstEvent * event) -{ - EventData data; - - data.event = event; - data.result = TRUE; - - gst_pad_forward (srcpad, (GstPadForwardFunction) event_forward_func, &data); - - gst_event_unref (event); - - return data.result; -} - -/** - * gst_collect_pads_src_event_default: - * @pads: the #GstCollectPads to use - * @pad: src #GstPad that received the event - * @event: event being processed - * - * Default #GstCollectPads event handling for the src pad of elements. - * Elements can chain up to this to let flushing seek event handling - * be done by #GstCollectPads. - * - * Since: 1.4 - */ -gboolean -gst_collect_pads_src_event_default (GstCollectPads * pads, GstPad * pad, - GstEvent * event) -{ - GstObject *parent; - gboolean res = TRUE; - - parent = GST_OBJECT_PARENT (pad); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK:{ - GstSeekFlags flags; - - pads->priv->eospads = 0; - - GST_INFO_OBJECT (pads, "starting seek"); - - gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL); - if (flags & GST_SEEK_FLAG_FLUSH) { - g_atomic_int_set (&pads->priv->seeking, TRUE); - g_atomic_int_set (&pads->priv->pending_flush_start, TRUE); - /* forward the seek upstream */ - res = forward_event_to_all_sinkpads (pad, event); - event = NULL; - if (!res) { - g_atomic_int_set (&pads->priv->seeking, FALSE); - g_atomic_int_set (&pads->priv->pending_flush_start, FALSE); - } - } - - GST_INFO_OBJECT (pads, "seek done, result: %d", res); - - break; - } - default: - break; - } - - if (event) - res = gst_pad_event_default (pad, parent, event); - - return res; -} - -static gboolean -gst_collect_pads_event_default_internal (GstCollectPads * pads, - GstCollectData * data, GstEvent * event, gpointer user_data) -{ - return gst_collect_pads_event_default (pads, data, event, FALSE); -} - -static gboolean -gst_collect_pads_event (GstPad * pad, GstObject * parent, GstEvent * event) -{ - gboolean res = FALSE, need_unlock = FALSE; - GstCollectData *data; - GstCollectPads *pads; - GstCollectPadsEventFunction event_func; - gpointer event_user_data; - - /* some magic to get the managing collect_pads */ - GST_OBJECT_LOCK (pad); - data = (GstCollectData *) gst_pad_get_element_private (pad); - if (G_UNLIKELY (data == NULL)) - goto pad_removed; - ref_data (data); - GST_OBJECT_UNLOCK (pad); - - res = FALSE; - - pads = data->collect; - - GST_DEBUG_OBJECT (data->pad, "Got %s event on sink pad", - GST_EVENT_TYPE_NAME (event)); - - GST_OBJECT_LOCK (pads); - event_func = pads->priv->event_func; - event_user_data = pads->priv->event_user_data; - GST_OBJECT_UNLOCK (pads); - - if (GST_EVENT_IS_SERIALIZED (event)) { - GST_COLLECT_PADS_STREAM_LOCK (pads); - need_unlock = TRUE; - } - - if (G_LIKELY (event_func)) { - res = event_func (pads, data, event, event_user_data); - } - - if (need_unlock) - GST_COLLECT_PADS_STREAM_UNLOCK (pads); - - unref_data (data); - return res; - - /* ERRORS */ -pad_removed: - { - GST_DEBUG ("%s got removed from collectpads", GST_OBJECT_NAME (pad)); - GST_OBJECT_UNLOCK (pad); - return FALSE; - } -} - -/** - * gst_collect_pads_query_default: - * @pads: the collectpads to use - * @data: collect data of corresponding pad - * @query: query being processed - * @discard: process but do not send event downstream - * - * Default #GstCollectPads query handling that elements should always - * chain up to to ensure proper operation. Element might however indicate - * query should not be forwarded downstream. - */ -gboolean -gst_collect_pads_query_default (GstCollectPads * pads, GstCollectData * data, - GstQuery * query, gboolean discard) -{ - gboolean res = TRUE; - GstObject *parent; - GstPad *pad; - - pad = data->pad; - parent = GST_OBJECT_PARENT (pad); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_SEEKING: - { - GstFormat format; - - /* don't pass it along as some (file)sink might claim it does - * whereas with a collectpads in between that will not likely work */ - gst_query_parse_seeking (query, &format, NULL, NULL, NULL); - gst_query_set_seeking (query, format, FALSE, 0, -1); - res = TRUE; - discard = TRUE; - break; - } - default: - break; - } - - if (!discard) - return gst_pad_query_default (pad, parent, query); - else - return res; -} - -static gboolean -gst_collect_pads_query_default_internal (GstCollectPads * pads, - GstCollectData * data, GstQuery * query, gpointer user_data) -{ - return gst_collect_pads_query_default (pads, data, query, FALSE); -} - -static gboolean -gst_collect_pads_query (GstPad * pad, GstObject * parent, GstQuery * query) -{ - gboolean res = FALSE, need_unlock = FALSE; - GstCollectData *data; - GstCollectPads *pads; - GstCollectPadsQueryFunction query_func; - gpointer query_user_data; - - GST_DEBUG_OBJECT (pad, "Got %s query on sink pad", - GST_QUERY_TYPE_NAME (query)); - - /* some magic to get the managing collect_pads */ - GST_OBJECT_LOCK (pad); - data = (GstCollectData *) gst_pad_get_element_private (pad); - if (G_UNLIKELY (data == NULL)) - goto pad_removed; - ref_data (data); - GST_OBJECT_UNLOCK (pad); - - pads = data->collect; - - GST_OBJECT_LOCK (pads); - query_func = pads->priv->query_func; - query_user_data = pads->priv->query_user_data; - GST_OBJECT_UNLOCK (pads); - - if (GST_QUERY_IS_SERIALIZED (query)) { - GST_COLLECT_PADS_STREAM_LOCK (pads); - need_unlock = TRUE; - } - - if (G_LIKELY (query_func)) { - res = query_func (pads, data, query, query_user_data); - } - - if (need_unlock) - GST_COLLECT_PADS_STREAM_UNLOCK (pads); - - unref_data (data); - return res; - - /* ERRORS */ -pad_removed: - { - GST_DEBUG ("%s got removed from collectpads", GST_OBJECT_NAME (pad)); - GST_OBJECT_UNLOCK (pad); - return FALSE; - } -} - - -/* For each buffer we receive we check if our collected condition is reached - * and if so we call the collected function. When this is done we check if - * data has been unqueued. If data is still queued we wait holding the stream - * lock to make sure no EOS event can happen while we are ready to be - * collected - */ -static GstFlowReturn -gst_collect_pads_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) -{ - GstCollectData *data; - GstCollectPads *pads; - GstFlowReturn ret; - GstBuffer **buffer_p; - guint32 cookie; - - GST_DEBUG ("Got buffer for pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - - /* some magic to get the managing collect_pads */ - GST_OBJECT_LOCK (pad); - data = (GstCollectData *) gst_pad_get_element_private (pad); - if (G_UNLIKELY (data == NULL)) - goto no_data; - ref_data (data); - GST_OBJECT_UNLOCK (pad); - - pads = data->collect; - - GST_COLLECT_PADS_STREAM_LOCK (pads); - /* if not started, bail out */ - if (G_UNLIKELY (!pads->priv->started)) - goto not_started; - /* check if this pad is flushing */ - if (G_UNLIKELY (GST_COLLECT_PADS_STATE_IS_SET (data, - GST_COLLECT_PADS_STATE_FLUSHING))) - goto flushing; - /* pad was EOS, we can refuse this data */ - if (G_UNLIKELY (GST_COLLECT_PADS_STATE_IS_SET (data, - GST_COLLECT_PADS_STATE_EOS))) - goto eos; - - /* see if we need to clip */ - if (pads->priv->clip_func) { - GstBuffer *outbuf = NULL; - ret = - pads->priv->clip_func (pads, data, buffer, &outbuf, - pads->priv->clip_user_data); - buffer = outbuf; - - if (G_UNLIKELY (outbuf == NULL)) - goto clipped; - - if (G_UNLIKELY (ret == GST_FLOW_EOS)) - goto eos; - else if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto error; - } - - GST_DEBUG_OBJECT (pads, "Queuing buffer %p for pad %s:%s", buffer, - GST_DEBUG_PAD_NAME (pad)); - - /* One more pad has data queued */ - if (GST_COLLECT_PADS_STATE_IS_SET (data, GST_COLLECT_PADS_STATE_WAITING)) - pads->priv->queuedpads++; - buffer_p = &data->buffer; - gst_buffer_replace (buffer_p, buffer); - - /* update segment last position if in TIME */ - if (G_LIKELY (data->segment.format == GST_FORMAT_TIME)) { - GstClockTime timestamp; - - timestamp = GST_BUFFER_DTS_OR_PTS (buffer); - - if (GST_CLOCK_TIME_IS_VALID (timestamp)) - data->segment.position = timestamp; - } - - /* While we have data queued on this pad try to collect stuff */ - do { - /* Check if our collected condition is matched and call the collected - * function if it is */ - ret = gst_collect_pads_check_collected (pads); - /* when an error occurs, we want to report this back to the caller ASAP - * without having to block if the buffer was not popped */ - if (G_UNLIKELY (ret != GST_FLOW_OK)) - goto error; - - /* data was consumed, we can exit and accept new data */ - if (data->buffer == NULL) - break; - - /* Having the _INIT here means we don't care about any broadcast up to here - * (most of which occur with STREAM_LOCK held, so could not have happened - * anyway). We do care about e.g. a remove initiated broadcast as of this - * point. Putting it here also makes this thread ignores any evt it raised - * itself (as is a usual WAIT semantic). - */ - GST_COLLECT_PADS_EVT_INIT (cookie); - - /* pad could be removed and re-added */ - unref_data (data); - GST_OBJECT_LOCK (pad); - if (G_UNLIKELY ((data = gst_pad_get_element_private (pad)) == NULL)) - goto pad_removed; - ref_data (data); - GST_OBJECT_UNLOCK (pad); - - GST_DEBUG_OBJECT (pads, "Pad %s:%s has a buffer queued, waiting", - GST_DEBUG_PAD_NAME (pad)); - - /* wait to be collected, this must happen from another thread triggered - * by the _chain function of another pad. We release the lock so we - * can get stopped or flushed as well. We can however not get EOS - * because we still hold the STREAM_LOCK. - */ - GST_COLLECT_PADS_STREAM_UNLOCK (pads); - GST_COLLECT_PADS_EVT_WAIT (pads, cookie); - GST_COLLECT_PADS_STREAM_LOCK (pads); - - GST_DEBUG_OBJECT (pads, "Pad %s:%s resuming", GST_DEBUG_PAD_NAME (pad)); - - /* after a signal, we could be stopped */ - if (G_UNLIKELY (!pads->priv->started)) - goto not_started; - /* check if this pad is flushing */ - if (G_UNLIKELY (GST_COLLECT_PADS_STATE_IS_SET (data, - GST_COLLECT_PADS_STATE_FLUSHING))) - goto flushing; - } - while (data->buffer != NULL); - -unlock_done: - GST_COLLECT_PADS_STREAM_UNLOCK (pads); - /* data is definitely NULL if pad_removed goto was run. */ - if (data) - unref_data (data); - if (buffer) - gst_buffer_unref (buffer); - return ret; - -pad_removed: - { - GST_WARNING ("%s got removed from collectpads", GST_OBJECT_NAME (pad)); - GST_OBJECT_UNLOCK (pad); - ret = GST_FLOW_NOT_LINKED; - goto unlock_done; - } - /* ERRORS */ -no_data: - { - GST_DEBUG ("%s got removed from collectpads", GST_OBJECT_NAME (pad)); - GST_OBJECT_UNLOCK (pad); - gst_buffer_unref (buffer); - return GST_FLOW_NOT_LINKED; - } -not_started: - { - GST_DEBUG ("not started"); - gst_collect_pads_clear (pads, data); - ret = GST_FLOW_FLUSHING; - goto unlock_done; - } -flushing: - { - GST_DEBUG ("pad %s:%s is flushing", GST_DEBUG_PAD_NAME (pad)); - gst_collect_pads_clear (pads, data); - ret = GST_FLOW_FLUSHING; - goto unlock_done; - } -eos: - { - /* we should not post an error for this, just inform upstream that - * we don't expect anything anymore */ - GST_DEBUG ("pad %s:%s is eos", GST_DEBUG_PAD_NAME (pad)); - ret = GST_FLOW_EOS; - goto unlock_done; - } -clipped: - { - GST_DEBUG ("clipped buffer on pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - ret = GST_FLOW_OK; - goto unlock_done; - } -error: - { - /* we print the error, the element should post a reasonable error - * message for fatal errors */ - GST_DEBUG ("collect failed, reason %d (%s)", ret, gst_flow_get_name (ret)); - gst_collect_pads_clear (pads, data); - goto unlock_done; - } -} diff --git a/libs/gst/base/gstcollectpads.h b/libs/gst/base/gstcollectpads.h deleted file mode 100644 index 13d60a17d9..0000000000 --- a/libs/gst/base/gstcollectpads.h +++ /dev/null @@ -1,456 +0,0 @@ -/* GStreamer - * Copyright (C) 2005 Wim Taymans <wim@fluendo.com> - * Copyright (C) 2008 Mark Nauwelaerts <mnauw@users.sourceforge.net> - * - * gstcollectpads.h: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_COLLECT_PADS_H__ -#define __GST_COLLECT_PADS_H__ - -#include <gst/gst.h> -#include <gst/base/base-prelude.h> - -G_BEGIN_DECLS - -#define GST_TYPE_COLLECT_PADS (gst_collect_pads_get_type()) -#define GST_COLLECT_PADS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_COLLECT_PADS,GstCollectPads)) -#define GST_COLLECT_PADS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_COLLECT_PADS,GstCollectPadsClass)) -#define GST_COLLECT_PADS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_COLLECT_PADS,GstCollectPadsClass)) -#define GST_IS_COLLECT_PADS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_COLLECT_PADS)) -#define GST_IS_COLLECT_PADS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_COLLECT_PADS)) - -typedef struct _GstCollectData GstCollectData; -typedef struct _GstCollectDataPrivate GstCollectDataPrivate; -typedef struct _GstCollectPads GstCollectPads; -typedef struct _GstCollectPadsPrivate GstCollectPadsPrivate; -typedef struct _GstCollectPadsClass GstCollectPadsClass; - -/** - * GstCollectDataDestroyNotify: - * @data: the #GstCollectData that will be freed - * - * A function that will be called when the #GstCollectData will be freed. - * It is passed the pointer to the structure and should free any custom - * memory and resources allocated for it. - */ -typedef void (*GstCollectDataDestroyNotify) (GstCollectData *data); - -/** - * GstCollectPadsStateFlags: - * @GST_COLLECT_PADS_STATE_EOS: Set if collectdata's pad is EOS. - * @GST_COLLECT_PADS_STATE_FLUSHING: Set if collectdata's pad is flushing. - * @GST_COLLECT_PADS_STATE_NEW_SEGMENT: Set if collectdata's pad received a - * new_segment event. - * @GST_COLLECT_PADS_STATE_WAITING: Set if collectdata's pad must be waited - * for when collecting. - * @GST_COLLECT_PADS_STATE_LOCKED: Set collectdata's pad WAITING state must - * not be changed. - * #GstCollectPadsStateFlags indicate private state of a collectdata('s pad). - */ -typedef enum { - GST_COLLECT_PADS_STATE_EOS = 1 << 0, - GST_COLLECT_PADS_STATE_FLUSHING = 1 << 1, - GST_COLLECT_PADS_STATE_NEW_SEGMENT = 1 << 2, - GST_COLLECT_PADS_STATE_WAITING = 1 << 3, - GST_COLLECT_PADS_STATE_LOCKED = 1 << 4 -} GstCollectPadsStateFlags; - -/** - * GST_COLLECT_PADS_STATE: - * @data: a #GstCollectData. - * - * A flags word containing #GstCollectPadsStateFlags flags set - * on this collected pad. - */ -#define GST_COLLECT_PADS_STATE(data) (((GstCollectData *) data)->state) -/** - * GST_COLLECT_PADS_STATE_IS_SET: - * @data: a #GstCollectData. - * @flag: the #GstCollectPadsStateFlags to check. - * - * Gives the status of a specific flag on a collected pad. - */ -#define GST_COLLECT_PADS_STATE_IS_SET(data,flag) !!(GST_COLLECT_PADS_STATE (data) & flag) -/** - * GST_COLLECT_PADS_STATE_SET: - * @data: a #GstCollectData. - * @flag: the #GstCollectPadsStateFlags to set. - * - * Sets a state flag on a collected pad. - */ -#define GST_COLLECT_PADS_STATE_SET(data,flag) (GST_COLLECT_PADS_STATE (data) |= flag) -/** - * GST_COLLECT_PADS_STATE_UNSET: - * @data: a #GstCollectData. - * @flag: the #GstCollectPadsStateFlags to clear. - * - * Clears a state flag on a collected pad. - */ -#define GST_COLLECT_PADS_STATE_UNSET(data,flag) (GST_COLLECT_PADS_STATE (data) &= ~(flag)) - -/** - * GST_COLLECT_PADS_DTS: - * @data: A #GstCollectData. - * - * Returns the DTS that has been converted to running time when using - * gst_collect_pads_clip_running_time(). Unlike the value saved into - * the buffer, this value is of type gint64 and may be negative. This allow - * properly handling streams with frame reordering where the first DTS may - * be negative. If the initial DTS was not set, this value will be - * set to %G_MININT64. - * - * Since: 1.6 - */ -#define GST_COLLECT_PADS_DTS(data) (((GstCollectData *) data)->ABI.abi.dts) - -/** - * GST_COLLECT_PADS_DTS_IS_VALID: - * @data: A #GstCollectData. - * - * Check if running DTS value store is valid. - * - * Since: 1.6 - */ -#define GST_COLLECT_PADS_DTS_IS_VALID(data) (GST_CLOCK_STIME_IS_VALID (GST_COLLECT_PADS_DTS (data))) - -/** - * GstCollectData: - * @collect: owner #GstCollectPads - * @pad: #GstPad managed by this data - * @buffer: currently queued buffer. - * @pos: position in the buffer - * @segment: last segment received. - * @dts: the signed version of the DTS converted to running time. To access - * this member, use %GST_COLLECT_PADS_DTS macro. (Since: 1.6) - * - * Structure used by the collect_pads. - */ -struct _GstCollectData -{ - /* with STREAM_LOCK of @collect */ - GstCollectPads *collect; - GstPad *pad; - GstBuffer *buffer; - guint pos; - GstSegment segment; - - /*< private >*/ - /* state: bitfield for easier extension; - * eos, flushing, new_segment, waiting */ - GstCollectPadsStateFlags state; - - GstCollectDataPrivate *priv; - - union { - struct { - /*< public >*/ - gint64 dts; - /*< private >*/ - } abi; - gpointer _gst_reserved[GST_PADDING]; - } ABI; -}; - -/** - * GstCollectPadsFunction: - * @pads: the #GstCollectPads that triggered the callback - * @user_data: user data passed to gst_collect_pads_set_function() - * - * A function that will be called when all pads have received data. - * - * Returns: %GST_FLOW_OK for success - */ -typedef GstFlowReturn (*GstCollectPadsFunction) (GstCollectPads *pads, gpointer user_data); - -/** - * GstCollectPadsBufferFunction: - * @pads: the #GstCollectPads that triggered the callback - * @data: the #GstCollectData of pad that has received the buffer - * @buffer: (transfer full): the #GstBuffer - * @user_data: user data passed to gst_collect_pads_set_buffer_function() - * - * A function that will be called when a (considered oldest) buffer can be muxed. - * If all pads have reached EOS, this function is called with %NULL @buffer - * and %NULL @data. - * - * Returns: %GST_FLOW_OK for success - */ -typedef GstFlowReturn (*GstCollectPadsBufferFunction) (GstCollectPads *pads, GstCollectData *data, - GstBuffer *buffer, gpointer user_data); - -/** - * GstCollectPadsCompareFunction: - * @pads: the #GstCollectPads that is comparing the timestamps - * @data1: the first #GstCollectData - * @timestamp1: the first timestamp - * @data2: the second #GstCollectData - * @timestamp2: the second timestamp - * @user_data: user data passed to gst_collect_pads_set_compare_function() - * - * A function for comparing two timestamps of buffers or newsegments collected on one pad. - * - * Returns: Integer less than zero when first timestamp is deemed older than the second one. - * Zero if the timestamps are deemed equally old. - * Integer greater than zero when second timestamp is deemed older than the first one. - */ -typedef gint (*GstCollectPadsCompareFunction) (GstCollectPads *pads, - GstCollectData * data1, GstClockTime timestamp1, - GstCollectData * data2, GstClockTime timestamp2, - gpointer user_data); - -/** - * GstCollectPadsEventFunction: - * @pads: the #GstCollectPads that triggered the callback - * @pad: the #GstPad that received an event - * @event: the #GstEvent received - * @user_data: user data passed to gst_collect_pads_set_event_function() - * - * A function that will be called while processing an event. It takes - * ownership of the event and is responsible for chaining up (to - * gst_collect_pads_event_default()) or dropping events (such typical cases - * being handled by the default handler). - * - * Returns: %TRUE if the pad could handle the event - */ -typedef gboolean (*GstCollectPadsEventFunction) (GstCollectPads *pads, GstCollectData * pad, - GstEvent * event, gpointer user_data); - - -/** - * GstCollectPadsQueryFunction: - * @pads: the #GstCollectPads that triggered the callback - * @pad: the #GstPad that received an event - * @query: the #GstEvent received - * @user_data: user data passed to gst_collect_pads_set_query_function() - * - * A function that will be called while processing a query. It takes - * ownership of the query and is responsible for chaining up (to - * events downstream (with gst_pad_event_default()). - * - * Returns: %TRUE if the pad could handle the event - */ -typedef gboolean (*GstCollectPadsQueryFunction) (GstCollectPads *pads, GstCollectData * pad, - GstQuery * query, gpointer user_data); - -/** - * GstCollectPadsClipFunction: - * @pads: a #GstCollectPads - * @data: a #GstCollectData - * @inbuffer: (transfer full): the input #GstBuffer - * @outbuffer: (out): the output #GstBuffer - * @user_data: user data - * - * A function that will be called when @inbuffer is received on the pad managed - * by @data in the collectpad object @pads. - * - * The function should use the segment of @data and the negotiated media type on - * the pad to perform clipping of @inbuffer. - * - * This function takes ownership of @inbuffer and should output a buffer in - * @outbuffer or return %NULL in @outbuffer if the buffer should be dropped. - * - * Returns: a #GstFlowReturn that corresponds to the result of clipping. - */ -typedef GstFlowReturn (*GstCollectPadsClipFunction) (GstCollectPads *pads, GstCollectData *data, - GstBuffer *inbuffer, GstBuffer **outbuffer, - gpointer user_data); - - -/** - * GstCollectPadsFlushFunction: - * @pads: a #GstCollectPads - * @user_data: user data - * - * A function that will be called while processing a flushing seek event. - * - * The function should flush any internal state of the element and the state of - * all the pads. It should clear only the state not directly managed by the - * @pads object. It is therefore not necessary to call - * gst_collect_pads_set_flushing nor gst_collect_pads_clear from this function. - * - * Since: 1.4 - */ -typedef void (*GstCollectPadsFlushFunction) (GstCollectPads *pads, gpointer user_data); - -/** - * GST_COLLECT_PADS_GET_STREAM_LOCK: - * @pads: a #GstCollectPads - * - * Get the stream lock of @pads. The stream lock is used to coordinate and - * serialize execution among the various streams being collected, and in - * protecting the resources used to accomplish this. - */ -#define GST_COLLECT_PADS_GET_STREAM_LOCK(pads) (&((GstCollectPads *)pads)->stream_lock) -/** - * GST_COLLECT_PADS_STREAM_LOCK: - * @pads: a #GstCollectPads - * - * Lock the stream lock of @pads. - */ -#define GST_COLLECT_PADS_STREAM_LOCK(pads) g_rec_mutex_lock(GST_COLLECT_PADS_GET_STREAM_LOCK (pads)) -/** - * GST_COLLECT_PADS_STREAM_UNLOCK: - * @pads: a #GstCollectPads - * - * Unlock the stream lock of @pads. - */ -#define GST_COLLECT_PADS_STREAM_UNLOCK(pads) g_rec_mutex_unlock(GST_COLLECT_PADS_GET_STREAM_LOCK (pads)) - -/** - * GstCollectPads: - * @data: (element-type GstBase.CollectData): #GList of #GstCollectData managed - * by this #GstCollectPads. - * - * Collectpads object. - */ -struct _GstCollectPads { - GstObject object; - - /*< public >*/ /* with LOCK and/or STREAM_LOCK */ - GSList *data; /* list of CollectData items */ - - /*< private >*/ - GRecMutex stream_lock; /* used to serialize collection among several streams */ - - GstCollectPadsPrivate *priv; - - gpointer _gst_reserved[GST_PADDING]; -}; - -struct _GstCollectPadsClass { - GstObjectClass parent_class; - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; -}; - -GST_BASE_API -GType gst_collect_pads_get_type (void); - -/* creating the object */ - -GST_BASE_API -GstCollectPads* gst_collect_pads_new (void); - -/* set the callbacks */ - -GST_BASE_API -void gst_collect_pads_set_function (GstCollectPads *pads, - GstCollectPadsFunction func, - gpointer user_data); -GST_BASE_API -void gst_collect_pads_set_buffer_function (GstCollectPads *pads, - GstCollectPadsBufferFunction func, - gpointer user_data); -GST_BASE_API -void gst_collect_pads_set_event_function (GstCollectPads *pads, - GstCollectPadsEventFunction func, - gpointer user_data); -GST_BASE_API -void gst_collect_pads_set_query_function (GstCollectPads *pads, - GstCollectPadsQueryFunction func, - gpointer user_data); -GST_BASE_API -void gst_collect_pads_set_compare_function (GstCollectPads *pads, - GstCollectPadsCompareFunction func, - gpointer user_data); -GST_BASE_API -void gst_collect_pads_set_clip_function (GstCollectPads *pads, - GstCollectPadsClipFunction clipfunc, - gpointer user_data); -GST_BASE_API -void gst_collect_pads_set_flush_function (GstCollectPads *pads, - GstCollectPadsFlushFunction func, - gpointer user_data); - -/* pad management */ - -GST_BASE_API -GstCollectData* gst_collect_pads_add_pad (GstCollectPads *pads, GstPad *pad, guint size, - GstCollectDataDestroyNotify destroy_notify, - gboolean lock); -GST_BASE_API -gboolean gst_collect_pads_remove_pad (GstCollectPads *pads, GstPad *pad); - -/* start/stop collection */ - -GST_BASE_API -void gst_collect_pads_start (GstCollectPads *pads); - -GST_BASE_API -void gst_collect_pads_stop (GstCollectPads *pads); - -GST_BASE_API -void gst_collect_pads_set_flushing (GstCollectPads *pads, gboolean flushing); - -/* get collected buffers */ - -GST_BASE_API -GstBuffer* gst_collect_pads_peek (GstCollectPads *pads, GstCollectData *data); - -GST_BASE_API -GstBuffer* gst_collect_pads_pop (GstCollectPads *pads, GstCollectData *data); - -/* get collected bytes */ - -GST_BASE_API -guint gst_collect_pads_available (GstCollectPads *pads); - -GST_BASE_API -guint gst_collect_pads_flush (GstCollectPads *pads, GstCollectData *data, - guint size); -GST_BASE_API -GstBuffer* gst_collect_pads_read_buffer (GstCollectPads * pads, GstCollectData * data, - guint size); -GST_BASE_API -GstBuffer* gst_collect_pads_take_buffer (GstCollectPads * pads, GstCollectData * data, - guint size); - -/* setting and unsetting waiting mode */ - -GST_BASE_API -void gst_collect_pads_set_waiting (GstCollectPads *pads, GstCollectData *data, - gboolean waiting); - -/* convenience helper */ - -GST_BASE_API -GstFlowReturn gst_collect_pads_clip_running_time (GstCollectPads * pads, - GstCollectData * cdata, - GstBuffer * buf, GstBuffer ** outbuf, - gpointer user_data); - -/* default handlers */ - -GST_BASE_API -gboolean gst_collect_pads_event_default (GstCollectPads * pads, GstCollectData * data, - GstEvent * event, gboolean discard); -GST_BASE_API -gboolean gst_collect_pads_src_event_default (GstCollectPads * pads, GstPad * pad, - GstEvent * event); -GST_BASE_API -gboolean gst_collect_pads_query_default (GstCollectPads * pads, GstCollectData * data, - GstQuery * query, gboolean discard); - - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstCollectPads, gst_object_unref) - -G_END_DECLS - -#endif /* __GST_COLLECT_PADS_H__ */ diff --git a/libs/gst/base/gstdataqueue.c b/libs/gst/base/gstdataqueue.c deleted file mode 100644 index ac93b8e346..0000000000 --- a/libs/gst/base/gstdataqueue.c +++ /dev/null @@ -1,812 +0,0 @@ -/* GStreamer - * Copyright (C) 2006 Edward Hervey <edward@fluendo.com> - * - * gstdataqueue.c: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * SECTION:gstdataqueue - * @title: GstDataQueue - * @short_description: Threadsafe queueing object - * - * #GstDataQueue is an object that handles threadsafe queueing of objects. It - * also provides size-related functionality. This object should be used for - * any #GstElement that wishes to provide some sort of queueing functionality. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <gst/gst.h> -#include "string.h" -#include "gstdataqueue.h" -#include "gstqueuearray.h" -#include "gst/glib-compat-private.h" - -GST_DEBUG_CATEGORY_STATIC (data_queue_debug); -#define GST_CAT_DEFAULT (data_queue_debug) -GST_DEBUG_CATEGORY_STATIC (data_queue_dataflow); - - -/* Queue signals and args */ -enum -{ - SIGNAL_EMPTY, - SIGNAL_FULL, - LAST_SIGNAL -}; - -enum -{ - PROP_0, - PROP_CUR_LEVEL_VISIBLE, - PROP_CUR_LEVEL_BYTES, - PROP_CUR_LEVEL_TIME - /* FILL ME */ -}; - -struct _GstDataQueuePrivate -{ - /* the array of data we're keeping our grubby hands on */ - GstQueueArray *queue; - - GstDataQueueSize cur_level; /* size of the queue */ - GstDataQueueCheckFullFunction checkfull; /* Callback to check if the queue is full */ - gpointer *checkdata; - - GMutex qlock; /* lock for queue (vs object lock) */ - gboolean waiting_add; - GCond item_add; /* signals buffers now available for reading */ - gboolean waiting_del; - GCond item_del; /* signals space now available for writing */ - gboolean flushing; /* indicates whether conditions where signalled because - * of external flushing */ - GstDataQueueFullCallback fullcallback; - GstDataQueueEmptyCallback emptycallback; -}; - -#define GST_DATA_QUEUE_MUTEX_LOCK(q) G_STMT_START { \ - GST_CAT_TRACE (data_queue_dataflow, \ - "locking qlock from thread %p", \ - g_thread_self ()); \ - g_mutex_lock (&q->priv->qlock); \ - GST_CAT_TRACE (data_queue_dataflow, \ - "locked qlock from thread %p", \ - g_thread_self ()); \ -} G_STMT_END - -#define GST_DATA_QUEUE_MUTEX_LOCK_CHECK(q, label) G_STMT_START { \ - GST_DATA_QUEUE_MUTEX_LOCK (q); \ - if (q->priv->flushing) \ - goto label; \ - } G_STMT_END - -#define GST_DATA_QUEUE_MUTEX_UNLOCK(q) G_STMT_START { \ - GST_CAT_TRACE (data_queue_dataflow, \ - "unlocking qlock from thread %p", \ - g_thread_self ()); \ - g_mutex_unlock (&q->priv->qlock); \ -} G_STMT_END - -#define STATUS(q, msg) \ - GST_CAT_LOG (data_queue_dataflow, \ - "queue:%p " msg ": %u visible items, %u " \ - "bytes, %"G_GUINT64_FORMAT \ - " ns, %u elements", \ - queue, \ - q->priv->cur_level.visible, \ - q->priv->cur_level.bytes, \ - q->priv->cur_level.time, \ - gst_queue_array_get_length (q->priv->queue)) - -static void gst_data_queue_finalize (GObject * object); - -static void gst_data_queue_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_data_queue_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); - -static guint gst_data_queue_signals[LAST_SIGNAL] = { 0 }; - -#define _do_init \ -{ \ - GST_DEBUG_CATEGORY_INIT (data_queue_debug, "dataqueue", 0, \ - "data queue object"); \ - GST_DEBUG_CATEGORY_INIT (data_queue_dataflow, "data_queue_dataflow", 0, \ - "dataflow inside the data queue object"); \ -} - -#define parent_class gst_data_queue_parent_class -G_DEFINE_TYPE_WITH_CODE (GstDataQueue, gst_data_queue, G_TYPE_OBJECT, - G_ADD_PRIVATE (GstDataQueue) _do_init); - -static void -gst_data_queue_class_init (GstDataQueueClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gobject_class->set_property = gst_data_queue_set_property; - gobject_class->get_property = gst_data_queue_get_property; - - /* signals */ - /** - * GstDataQueue::empty: (skip) - * @queue: the queue instance - * - * Reports that the queue became empty (empty). - * A queue is empty if the total amount of visible items inside it (num-visible, time, - * size) is lower than the boundary values which can be set through the GObject - * properties. - */ - gst_data_queue_signals[SIGNAL_EMPTY] = - g_signal_new ("empty", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (GstDataQueueClass, empty), NULL, NULL, - NULL, G_TYPE_NONE, 0); - - /** - * GstDataQueue::full: (skip) - * @queue: the queue instance - * - * Reports that the queue became full (full). - * A queue is full if the total amount of data inside it (num-visible, time, - * size) is higher than the boundary values which can be set through the GObject - * properties. - */ - gst_data_queue_signals[SIGNAL_FULL] = - g_signal_new ("full", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (GstDataQueueClass, full), NULL, NULL, - NULL, G_TYPE_NONE, 0); - - /* properties */ - g_object_class_install_property (gobject_class, PROP_CUR_LEVEL_BYTES, - g_param_spec_uint ("current-level-bytes", "Current level (kB)", - "Current amount of data in the queue (bytes)", - 0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_CUR_LEVEL_VISIBLE, - g_param_spec_uint ("current-level-visible", - "Current level (visible items)", - "Current number of visible items in the queue", 0, G_MAXUINT, 0, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_CUR_LEVEL_TIME, - g_param_spec_uint64 ("current-level-time", "Current level (ns)", - "Current amount of data in the queue (in ns)", 0, G_MAXUINT64, 0, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - gobject_class->finalize = gst_data_queue_finalize; -} - -static void -gst_data_queue_init (GstDataQueue * queue) -{ - queue->priv = gst_data_queue_get_instance_private (queue); - - queue->priv->cur_level.visible = 0; /* no content */ - queue->priv->cur_level.bytes = 0; /* no content */ - queue->priv->cur_level.time = 0; /* no content */ - - queue->priv->checkfull = NULL; - - g_mutex_init (&queue->priv->qlock); - g_cond_init (&queue->priv->item_add); - g_cond_init (&queue->priv->item_del); - queue->priv->queue = gst_queue_array_new (50); - - GST_DEBUG ("initialized queue's not_empty & not_full conditions"); -} - -/** - * gst_data_queue_new: (skip) - * @checkfull: the callback used to tell if the element considers the queue full - * or not. - * @fullcallback: the callback which will be called when the queue is considered full. - * @emptycallback: the callback which will be called when the queue is considered empty. - * @checkdata: a #gpointer that will be passed to the @checkfull, @fullcallback, - * and @emptycallback callbacks. - * - * Creates a new #GstDataQueue. If @fullcallback or @emptycallback are supplied, then - * the #GstDataQueue will call the respective callback to signal full or empty condition. - * If the callbacks are NULL the #GstDataQueue will instead emit 'full' and 'empty' - * signals. - * - * Returns: a new #GstDataQueue. - * - * Since: 1.2 - */ -GstDataQueue * -gst_data_queue_new (GstDataQueueCheckFullFunction checkfull, - GstDataQueueFullCallback fullcallback, - GstDataQueueEmptyCallback emptycallback, gpointer checkdata) -{ - GstDataQueue *ret; - - g_return_val_if_fail (checkfull != NULL, NULL); - - ret = g_object_new (GST_TYPE_DATA_QUEUE, NULL); - ret->priv->checkfull = checkfull; - ret->priv->checkdata = checkdata; - ret->priv->fullcallback = fullcallback; - ret->priv->emptycallback = emptycallback; - - return ret; -} - -static void -gst_data_queue_cleanup (GstDataQueue * queue) -{ - GstDataQueuePrivate *priv = queue->priv; - - while (!gst_queue_array_is_empty (priv->queue)) { - GstDataQueueItem *item = gst_queue_array_pop_head (priv->queue); - - /* Just call the destroy notify on the item */ - item->destroy (item); - } - priv->cur_level.visible = 0; - priv->cur_level.bytes = 0; - priv->cur_level.time = 0; -} - -/* called only once, as opposed to dispose */ -static void -gst_data_queue_finalize (GObject * object) -{ - GstDataQueue *queue = GST_DATA_QUEUE (object); - GstDataQueuePrivate *priv = queue->priv; - - GST_DEBUG ("finalizing queue"); - - gst_data_queue_cleanup (queue); - gst_queue_array_free (priv->queue); - - GST_DEBUG ("free mutex"); - g_mutex_clear (&priv->qlock); - GST_DEBUG ("done free mutex"); - - g_cond_clear (&priv->item_add); - g_cond_clear (&priv->item_del); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static inline void -gst_data_queue_locked_flush (GstDataQueue * queue) -{ - GstDataQueuePrivate *priv = queue->priv; - - STATUS (queue, "before flushing"); - gst_data_queue_cleanup (queue); - STATUS (queue, "after flushing"); - /* we deleted something... */ - if (priv->waiting_del) - g_cond_signal (&priv->item_del); -} - -static inline gboolean -gst_data_queue_locked_is_empty (GstDataQueue * queue) -{ - GstDataQueuePrivate *priv = queue->priv; - - return (gst_queue_array_get_length (priv->queue) == 0); -} - -static inline gboolean -gst_data_queue_locked_is_full (GstDataQueue * queue) -{ - GstDataQueuePrivate *priv = queue->priv; - - return priv->checkfull (queue, priv->cur_level.visible, - priv->cur_level.bytes, priv->cur_level.time, priv->checkdata); -} - -/** - * gst_data_queue_flush: (skip) - * @queue: a #GstDataQueue. - * - * Flushes all the contents of the @queue. Any call to #gst_data_queue_push and - * #gst_data_queue_pop will be released. - * MT safe. - * - * Since: 1.2 - */ -void -gst_data_queue_flush (GstDataQueue * queue) -{ - GST_DEBUG ("queue:%p", queue); - GST_DATA_QUEUE_MUTEX_LOCK (queue); - gst_data_queue_locked_flush (queue); - GST_DATA_QUEUE_MUTEX_UNLOCK (queue); -} - -/** - * gst_data_queue_is_empty: (skip) - * @queue: a #GstDataQueue. - * - * Queries if there are any items in the @queue. - * MT safe. - * - * Returns: %TRUE if @queue is empty. - * - * Since: 1.2 - */ -gboolean -gst_data_queue_is_empty (GstDataQueue * queue) -{ - gboolean res; - - GST_DATA_QUEUE_MUTEX_LOCK (queue); - res = gst_data_queue_locked_is_empty (queue); - GST_DATA_QUEUE_MUTEX_UNLOCK (queue); - - return res; -} - -/** - * gst_data_queue_is_full: (skip) - * @queue: a #GstDataQueue. - * - * Queries if @queue is full. This check will be done using the - * #GstDataQueueCheckFullFunction registered with @queue. - * MT safe. - * - * Returns: %TRUE if @queue is full. - * - * Since: 1.2 - */ -gboolean -gst_data_queue_is_full (GstDataQueue * queue) -{ - gboolean res; - - GST_DATA_QUEUE_MUTEX_LOCK (queue); - res = gst_data_queue_locked_is_full (queue); - GST_DATA_QUEUE_MUTEX_UNLOCK (queue); - - return res; -} - -/** - * gst_data_queue_set_flushing: (skip) - * @queue: a #GstDataQueue. - * @flushing: a #gboolean stating if the queue will be flushing or not. - * - * Sets the queue to flushing state if @flushing is %TRUE. If set to flushing - * state, any incoming data on the @queue will be discarded. Any call currently - * blocking on #gst_data_queue_push or #gst_data_queue_pop will return straight - * away with a return value of %FALSE. While the @queue is in flushing state, - * all calls to those two functions will return %FALSE. - * - * MT Safe. - * - * Since: 1.2 - */ -void -gst_data_queue_set_flushing (GstDataQueue * queue, gboolean flushing) -{ - GstDataQueuePrivate *priv = queue->priv; - - GST_DEBUG ("queue:%p , flushing:%d", queue, flushing); - - GST_DATA_QUEUE_MUTEX_LOCK (queue); - priv->flushing = flushing; - if (flushing) { - /* release push/pop functions */ - if (priv->waiting_add) - g_cond_signal (&priv->item_add); - if (priv->waiting_del) - g_cond_signal (&priv->item_del); - } - GST_DATA_QUEUE_MUTEX_UNLOCK (queue); -} - -static void -gst_data_queue_push_force_unlocked (GstDataQueue * queue, - GstDataQueueItem * item) -{ - GstDataQueuePrivate *priv = queue->priv; - - gst_queue_array_push_tail (priv->queue, item); - - if (item->visible) - priv->cur_level.visible++; - priv->cur_level.bytes += item->size; - priv->cur_level.time += item->duration; -} - -/** - * gst_data_queue_push_force: (skip) - * @queue: a #GstDataQueue. - * @item: a #GstDataQueueItem. - * - * Pushes a #GstDataQueueItem (or a structure that begins with the same fields) - * on the @queue. It ignores if the @queue is full or not and forces the @item - * to be pushed anyway. - * MT safe. - * - * Note that this function has slightly different semantics than gst_pad_push() - * and gst_pad_push_event(): this function only takes ownership of @item and - * the #GstMiniObject contained in @item if the push was successful. If %FALSE - * is returned, the caller is responsible for freeing @item and its contents. - * - * Returns: %TRUE if the @item was successfully pushed on the @queue. - * - * Since: 1.2 - */ -gboolean -gst_data_queue_push_force (GstDataQueue * queue, GstDataQueueItem * item) -{ - GstDataQueuePrivate *priv = queue->priv; - - g_return_val_if_fail (GST_IS_DATA_QUEUE (queue), FALSE); - g_return_val_if_fail (item != NULL, FALSE); - - GST_DATA_QUEUE_MUTEX_LOCK_CHECK (queue, flushing); - - STATUS (queue, "before pushing"); - gst_data_queue_push_force_unlocked (queue, item); - STATUS (queue, "after pushing"); - if (priv->waiting_add) - g_cond_signal (&priv->item_add); - - GST_DATA_QUEUE_MUTEX_UNLOCK (queue); - - return TRUE; - - /* ERRORS */ -flushing: - { - GST_DEBUG ("queue:%p, we are flushing", queue); - GST_DATA_QUEUE_MUTEX_UNLOCK (queue); - return FALSE; - } -} - -/** - * gst_data_queue_push: (skip) - * @queue: a #GstDataQueue. - * @item: a #GstDataQueueItem. - * - * Pushes a #GstDataQueueItem (or a structure that begins with the same fields) - * on the @queue. If the @queue is full, the call will block until space is - * available, OR the @queue is set to flushing state. - * MT safe. - * - * Note that this function has slightly different semantics than gst_pad_push() - * and gst_pad_push_event(): this function only takes ownership of @item and - * the #GstMiniObject contained in @item if the push was successful. If %FALSE - * is returned, the caller is responsible for freeing @item and its contents. - * - * Returns: %TRUE if the @item was successfully pushed on the @queue. - * - * Since: 1.2 - */ -gboolean -gst_data_queue_push (GstDataQueue * queue, GstDataQueueItem * item) -{ - GstDataQueuePrivate *priv = queue->priv; - - g_return_val_if_fail (GST_IS_DATA_QUEUE (queue), FALSE); - g_return_val_if_fail (item != NULL, FALSE); - - GST_DATA_QUEUE_MUTEX_LOCK_CHECK (queue, flushing); - - STATUS (queue, "before pushing"); - - /* We ALWAYS need to check for queue fillness */ - if (gst_data_queue_locked_is_full (queue)) { - GST_DATA_QUEUE_MUTEX_UNLOCK (queue); - if (G_LIKELY (priv->fullcallback)) - priv->fullcallback (queue, priv->checkdata); - else - g_signal_emit (queue, gst_data_queue_signals[SIGNAL_FULL], 0); - GST_DATA_QUEUE_MUTEX_LOCK_CHECK (queue, flushing); - - /* signal might have removed some items */ - while (gst_data_queue_locked_is_full (queue)) { - priv->waiting_del = TRUE; - g_cond_wait (&priv->item_del, &priv->qlock); - priv->waiting_del = FALSE; - if (priv->flushing) - goto flushing; - } - } - - gst_data_queue_push_force_unlocked (queue, item); - - STATUS (queue, "after pushing"); - if (priv->waiting_add) - g_cond_signal (&priv->item_add); - - GST_DATA_QUEUE_MUTEX_UNLOCK (queue); - - return TRUE; - - /* ERRORS */ -flushing: - { - GST_DEBUG ("queue:%p, we are flushing", queue); - GST_DATA_QUEUE_MUTEX_UNLOCK (queue); - return FALSE; - } -} - -static gboolean -_gst_data_queue_wait_non_empty (GstDataQueue * queue) -{ - GstDataQueuePrivate *priv = queue->priv; - - while (gst_data_queue_locked_is_empty (queue)) { - priv->waiting_add = TRUE; - g_cond_wait (&priv->item_add, &priv->qlock); - priv->waiting_add = FALSE; - if (priv->flushing) - return FALSE; - } - return TRUE; -} - -/** - * gst_data_queue_pop: (skip) - * @queue: a #GstDataQueue. - * @item: (out): pointer to store the returned #GstDataQueueItem. - * - * Retrieves the first @item available on the @queue. If the queue is currently - * empty, the call will block until at least one item is available, OR the - * @queue is set to the flushing state. - * MT safe. - * - * Returns: %TRUE if an @item was successfully retrieved from the @queue. - * - * Since: 1.2 - */ -gboolean -gst_data_queue_pop (GstDataQueue * queue, GstDataQueueItem ** item) -{ - GstDataQueuePrivate *priv = queue->priv; - - g_return_val_if_fail (GST_IS_DATA_QUEUE (queue), FALSE); - g_return_val_if_fail (item != NULL, FALSE); - - GST_DATA_QUEUE_MUTEX_LOCK_CHECK (queue, flushing); - - STATUS (queue, "before popping"); - - if (gst_data_queue_locked_is_empty (queue)) { - GST_DATA_QUEUE_MUTEX_UNLOCK (queue); - if (G_LIKELY (priv->emptycallback)) - priv->emptycallback (queue, priv->checkdata); - else - g_signal_emit (queue, gst_data_queue_signals[SIGNAL_EMPTY], 0); - GST_DATA_QUEUE_MUTEX_LOCK_CHECK (queue, flushing); - - if (!_gst_data_queue_wait_non_empty (queue)) - goto flushing; - } - - /* Get the item from the GQueue */ - *item = gst_queue_array_pop_head (priv->queue); - - /* update current level counter */ - if ((*item)->visible) - priv->cur_level.visible--; - priv->cur_level.bytes -= (*item)->size; - priv->cur_level.time -= (*item)->duration; - - STATUS (queue, "after popping"); - if (priv->waiting_del) - g_cond_signal (&priv->item_del); - - GST_DATA_QUEUE_MUTEX_UNLOCK (queue); - - return TRUE; - - /* ERRORS */ -flushing: - { - GST_DEBUG ("queue:%p, we are flushing", queue); - GST_DATA_QUEUE_MUTEX_UNLOCK (queue); - return FALSE; - } -} - -static gint -is_of_type (gconstpointer a, gconstpointer b) -{ - return !G_TYPE_CHECK_INSTANCE_TYPE (a, GPOINTER_TO_SIZE (b)); -} - -/** - * gst_data_queue_peek: (skip) - * @queue: a #GstDataQueue. - * @item: (out): pointer to store the returned #GstDataQueueItem. - * - * Retrieves the first @item available on the @queue without removing it. - * If the queue is currently empty, the call will block until at least - * one item is available, OR the @queue is set to the flushing state. - * MT safe. - * - * Returns: %TRUE if an @item was successfully retrieved from the @queue. - * - * Since: 1.2 - */ -gboolean -gst_data_queue_peek (GstDataQueue * queue, GstDataQueueItem ** item) -{ - GstDataQueuePrivate *priv = queue->priv; - - g_return_val_if_fail (GST_IS_DATA_QUEUE (queue), FALSE); - g_return_val_if_fail (item != NULL, FALSE); - - GST_DATA_QUEUE_MUTEX_LOCK_CHECK (queue, flushing); - - STATUS (queue, "before peeking"); - - if (gst_data_queue_locked_is_empty (queue)) { - GST_DATA_QUEUE_MUTEX_UNLOCK (queue); - if (G_LIKELY (priv->emptycallback)) - priv->emptycallback (queue, priv->checkdata); - else - g_signal_emit (queue, gst_data_queue_signals[SIGNAL_EMPTY], 0); - GST_DATA_QUEUE_MUTEX_LOCK_CHECK (queue, flushing); - - if (!_gst_data_queue_wait_non_empty (queue)) - goto flushing; - } - - /* Get the item from the GQueue */ - *item = gst_queue_array_peek_head (priv->queue); - - STATUS (queue, "after peeking"); - GST_DATA_QUEUE_MUTEX_UNLOCK (queue); - - return TRUE; - - /* ERRORS */ -flushing: - { - GST_DEBUG ("queue:%p, we are flushing", queue); - GST_DATA_QUEUE_MUTEX_UNLOCK (queue); - return FALSE; - } -} - -/** - * gst_data_queue_drop_head: (skip) - * @queue: The #GstDataQueue to drop an item from. - * @type: The #GType of the item to drop. - * - * Pop and unref the head-most #GstMiniObject with the given #GType. - * - * Returns: %TRUE if an element was removed. - * - * Since: 1.2 - */ -gboolean -gst_data_queue_drop_head (GstDataQueue * queue, GType type) -{ - gboolean res = FALSE; - GstDataQueueItem *leak = NULL; - guint idx; - GstDataQueuePrivate *priv = queue->priv; - - g_return_val_if_fail (GST_IS_DATA_QUEUE (queue), FALSE); - - GST_DEBUG ("queue:%p", queue); - - GST_DATA_QUEUE_MUTEX_LOCK (queue); - idx = gst_queue_array_find (priv->queue, is_of_type, GSIZE_TO_POINTER (type)); - - if (idx == -1) - goto done; - - leak = gst_queue_array_drop_element (priv->queue, idx); - - if (leak->visible) - priv->cur_level.visible--; - priv->cur_level.bytes -= leak->size; - priv->cur_level.time -= leak->duration; - - leak->destroy (leak); - - res = TRUE; - -done: - GST_DATA_QUEUE_MUTEX_UNLOCK (queue); - - GST_DEBUG ("queue:%p , res:%d", queue, res); - - return res; -} - -/** - * gst_data_queue_limits_changed: (skip) - * @queue: The #GstDataQueue - * - * Inform the queue that the limits for the fullness check have changed and that - * any blocking gst_data_queue_push() should be unblocked to recheck the limits. - * - * Since: 1.2 - */ -void -gst_data_queue_limits_changed (GstDataQueue * queue) -{ - GstDataQueuePrivate *priv = queue->priv; - - g_return_if_fail (GST_IS_DATA_QUEUE (queue)); - - GST_DATA_QUEUE_MUTEX_LOCK (queue); - if (priv->waiting_del) { - GST_DEBUG ("signal del"); - g_cond_signal (&priv->item_del); - } - GST_DATA_QUEUE_MUTEX_UNLOCK (queue); -} - -/** - * gst_data_queue_get_level: (skip) - * @queue: The #GstDataQueue - * @level: (out): the location to store the result - * - * Get the current level of the queue. - * - * Since: 1.2 - */ -void -gst_data_queue_get_level (GstDataQueue * queue, GstDataQueueSize * level) -{ - GstDataQueuePrivate *priv = queue->priv; - - memcpy (level, (&priv->cur_level), sizeof (GstDataQueueSize)); -} - -static void -gst_data_queue_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec) -{ - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_data_queue_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec) -{ - GstDataQueue *queue = GST_DATA_QUEUE (object); - GstDataQueuePrivate *priv = queue->priv; - - GST_DATA_QUEUE_MUTEX_LOCK (queue); - - switch (prop_id) { - case PROP_CUR_LEVEL_BYTES: - g_value_set_uint (value, priv->cur_level.bytes); - break; - case PROP_CUR_LEVEL_VISIBLE: - g_value_set_uint (value, priv->cur_level.visible); - break; - case PROP_CUR_LEVEL_TIME: - g_value_set_uint64 (value, priv->cur_level.time); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - - GST_DATA_QUEUE_MUTEX_UNLOCK (queue); -} diff --git a/libs/gst/base/gstdataqueue.h b/libs/gst/base/gstdataqueue.h deleted file mode 100644 index e814a2b693..0000000000 --- a/libs/gst/base/gstdataqueue.h +++ /dev/null @@ -1,184 +0,0 @@ -/* GStreamer - * Copyright (C) 2006 Edward Hervey <edward@fluendo.com> - * - * gstdataqueue.h: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - - -#ifndef __GST_DATA_QUEUE_H__ -#define __GST_DATA_QUEUE_H__ - -#include <gst/gst.h> -#include <gst/base/base-prelude.h> - -G_BEGIN_DECLS -#define GST_TYPE_DATA_QUEUE \ - (gst_data_queue_get_type()) -#define GST_DATA_QUEUE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DATA_QUEUE,GstDataQueue)) -#define GST_DATA_QUEUE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DATA_QUEUE,GstDataQueueClass)) -#define GST_IS_DATA_QUEUE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DATA_QUEUE)) -#define GST_IS_DATA_QUEUE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DATA_QUEUE)) - -typedef struct _GstDataQueue GstDataQueue; -typedef struct _GstDataQueueClass GstDataQueueClass; -typedef struct _GstDataQueueSize GstDataQueueSize; -typedef struct _GstDataQueueItem GstDataQueueItem; -typedef struct _GstDataQueuePrivate GstDataQueuePrivate; - -/** - * GstDataQueueItem: (skip) - * @object: the #GstMiniObject to queue. - * @size: the size in bytes of the miniobject. - * @duration: the duration in #GstClockTime of the miniobject. Can not be - * %GST_CLOCK_TIME_NONE. - * @visible: %TRUE if @object should be considered as a visible object. - * @destroy: The #GDestroyNotify function to use to free the #GstDataQueueItem. - * This function should also drop the reference to @object the owner of the - * #GstDataQueueItem is assumed to hold. - * - * Structure used by #GstDataQueue. You can supply a different structure, as - * long as the top of the structure is identical to this structure. - */ - -struct _GstDataQueueItem -{ - GstMiniObject *object; - guint size; - guint64 duration; - gboolean visible; - - /* user supplied destroy function */ - GDestroyNotify destroy; - - /* < private > */ - gpointer _gst_reserved[GST_PADDING]; -}; - -/** - * GstDataQueueSize: (skip) - * @visible: number of buffers - * @bytes: number of bytes - * @time: amount of time - * - * Structure describing the size of a queue. - */ -struct _GstDataQueueSize -{ - guint visible; - guint bytes; - guint64 time; -}; - -/** - * GstDataQueueCheckFullFunction: (skip) - * @queue: a #GstDataQueue. - * @visible: The number of visible items currently in the queue. - * @bytes: The amount of bytes currently in the queue. - * @time: The accumulated duration of the items currently in the queue. - * @checkdata: The #gpointer registered when the #GstDataQueue was created. - * - * The prototype of the function used to inform the queue that it should be - * considered as full. - * - * Returns: %TRUE if the queue should be considered full. - */ -typedef gboolean (*GstDataQueueCheckFullFunction) (GstDataQueue * queue, - guint visible, guint bytes, guint64 time, gpointer checkdata); - -typedef void (*GstDataQueueFullCallback) (GstDataQueue * queue, gpointer checkdata); -typedef void (*GstDataQueueEmptyCallback) (GstDataQueue * queue, gpointer checkdata); - -/** - * GstDataQueue: - * @object: the parent structure - * - * Opaque #GstDataQueue structure. - */ -struct _GstDataQueue -{ - GObject object; - - /*< private >*/ - GstDataQueuePrivate *priv; - gpointer _gst_reserved[GST_PADDING]; -}; - -/** - * GstDataQueueClass: - */ -struct _GstDataQueueClass -{ - GObjectClass parent_class; - - /* signals */ - void (*empty) (GstDataQueue * queue); - void (*full) (GstDataQueue * queue); - - gpointer _gst_reserved[GST_PADDING]; -}; - -GST_BASE_API -GType gst_data_queue_get_type (void); - -GST_BASE_API -GstDataQueue * gst_data_queue_new (GstDataQueueCheckFullFunction checkfull, - GstDataQueueFullCallback fullcallback, - GstDataQueueEmptyCallback emptycallback, - gpointer checkdata) G_GNUC_MALLOC; -GST_BASE_API -gboolean gst_data_queue_push (GstDataQueue * queue, GstDataQueueItem * item); - -GST_BASE_API -gboolean gst_data_queue_push_force (GstDataQueue * queue, GstDataQueueItem * item); - -GST_BASE_API -gboolean gst_data_queue_pop (GstDataQueue * queue, GstDataQueueItem ** item); - -GST_BASE_API -gboolean gst_data_queue_peek (GstDataQueue * queue, GstDataQueueItem ** item); - -GST_BASE_API -void gst_data_queue_flush (GstDataQueue * queue); - -GST_BASE_API -void gst_data_queue_set_flushing (GstDataQueue * queue, gboolean flushing); - -GST_BASE_API -gboolean gst_data_queue_drop_head (GstDataQueue * queue, GType type); - -GST_BASE_API -gboolean gst_data_queue_is_full (GstDataQueue * queue); - -GST_BASE_API -gboolean gst_data_queue_is_empty (GstDataQueue * queue); - -GST_BASE_API -void gst_data_queue_get_level (GstDataQueue * queue, GstDataQueueSize *level); - -GST_BASE_API -void gst_data_queue_limits_changed (GstDataQueue * queue); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstDataQueue, gst_object_unref) - -G_END_DECLS - -#endif /* __GST_DATA_QUEUE_H__ */ diff --git a/libs/gst/base/gstflowcombiner.c b/libs/gst/base/gstflowcombiner.c deleted file mode 100644 index d353eae4aa..0000000000 --- a/libs/gst/base/gstflowcombiner.c +++ /dev/null @@ -1,355 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2014 Samsung Electronics. All rights reserved. - * Author: Thiago Santos <ts.santos@sisa.samsung.com> - * - * gstflowcombiner.c: utility to combine multiple flow returns into a single one - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * SECTION:gstflowcombiner - * @title: GstFlowCombiner - * @short_description: Utility to combine multiple flow returns into one - * - * Utility struct to help handling #GstFlowReturn combination. Useful for - * #GstElement<!-- -->s that have multiple source pads and need to combine - * the different #GstFlowReturn for those pads. - * - * #GstFlowCombiner works by using the last #GstFlowReturn for all #GstPad - * it has in its list and computes the combined return value and provides - * it to the caller. - * - * To add a new pad to the #GstFlowCombiner use gst_flow_combiner_add_pad(). - * The new #GstPad is stored with a default value of %GST_FLOW_OK. - * - * In case you want a #GstPad to be removed, use gst_flow_combiner_remove_pad(). - * - * Please be aware that this struct isn't thread safe as its designed to be - * used by demuxers, those usually will have a single thread operating it. - * - * These functions will take refs on the passed #GstPad<!-- -->s. - * - * Aside from reducing the user's code size, the main advantage of using this - * helper struct is to follow the standard rules for #GstFlowReturn combination. - * These rules are: - * - * * %GST_FLOW_EOS: only if all returns are EOS too - * * %GST_FLOW_NOT_LINKED: only if all returns are NOT_LINKED too - * * %GST_FLOW_ERROR or below: if at least one returns an error return - * * %GST_FLOW_NOT_NEGOTIATED: if at least one returns a not-negotiated return - * * %GST_FLOW_FLUSHING: if at least one returns flushing - * * %GST_FLOW_OK: otherwise - * - * %GST_FLOW_ERROR or below, GST_FLOW_NOT_NEGOTIATED and GST_FLOW_FLUSHING are - * returned immediately from the gst_flow_combiner_update_flow() function. - * - * Since: 1.4 - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <gst/gst.h> -#include "gstflowcombiner.h" - -struct _GstFlowCombiner -{ - GQueue pads; - - GstFlowReturn last_ret; - gint ref_count; -}; - -GST_DEBUG_CATEGORY_STATIC (flowcombiner_dbg); -#define GST_CAT_DEFAULT flowcombiner_dbg - -G_DEFINE_BOXED_TYPE_WITH_CODE (GstFlowCombiner, gst_flow_combiner, - (GBoxedCopyFunc) gst_flow_combiner_ref, - (GBoxedFreeFunc) gst_flow_combiner_unref, - GST_DEBUG_CATEGORY_INIT (flowcombiner_dbg, "flowcombiner", 0, - "Flow Combiner")); - -/** - * gst_flow_combiner_new: - * - * Creates a new #GstFlowCombiner, use gst_flow_combiner_free() to free it. - * - * Returns: A new #GstFlowCombiner - * Since: 1.4 - */ -GstFlowCombiner * -gst_flow_combiner_new (void) -{ - GstFlowCombiner *combiner = g_slice_new (GstFlowCombiner); - - g_queue_init (&combiner->pads); - combiner->last_ret = GST_FLOW_OK; - g_atomic_int_set (&combiner->ref_count, 1); - - /* Make sure debug category is initialised */ - gst_flow_combiner_get_type (); - - return combiner; -} - -/** - * gst_flow_combiner_free: - * @combiner: the #GstFlowCombiner to free - * - * Frees a #GstFlowCombiner struct and all its internal data. - * - * Since: 1.4 - */ -void -gst_flow_combiner_free (GstFlowCombiner * combiner) -{ - gst_flow_combiner_unref (combiner); -} - -/** - * gst_flow_combiner_ref: - * @combiner: the #GstFlowCombiner to add a reference to. - * - * Increments the reference count on the #GstFlowCombiner. - * - * Returns: the #GstFlowCombiner. - * - * Since: 1.12.1 - */ -GstFlowCombiner * -gst_flow_combiner_ref (GstFlowCombiner * combiner) -{ - g_return_val_if_fail (combiner != NULL, NULL); - - g_atomic_int_inc (&combiner->ref_count); - - return combiner; -} - -/** - * gst_flow_combiner_unref: - * @combiner: the #GstFlowCombiner to unreference. - * - * Decrements the reference count on the #GstFlowCombiner. - * - * Since: 1.12.1 - */ -void -gst_flow_combiner_unref (GstFlowCombiner * combiner) -{ - g_return_if_fail (combiner != NULL); - g_return_if_fail (combiner->ref_count > 0); - - if (g_atomic_int_dec_and_test (&combiner->ref_count)) { - GstPad *pad; - - while ((pad = g_queue_pop_head (&combiner->pads))) - gst_object_unref (pad); - - g_slice_free (GstFlowCombiner, combiner); - } -} - -/** - * gst_flow_combiner_clear: - * @combiner: the #GstFlowCombiner to clear - * - * Removes all pads from a #GstFlowCombiner and resets it to its initial state. - * - * Since: 1.6 - */ -void -gst_flow_combiner_clear (GstFlowCombiner * combiner) -{ - GstPad *pad; - - g_return_if_fail (combiner != NULL); - - GST_DEBUG ("%p clearing", combiner); - - while ((pad = g_queue_pop_head (&combiner->pads))) - gst_object_unref (pad); - combiner->last_ret = GST_FLOW_OK; -} - -/** - * gst_flow_combiner_reset: - * @combiner: the #GstFlowCombiner to clear - * - * Reset flow combiner and all pads to their initial state without removing pads. - * - * Since: 1.6 - */ -void -gst_flow_combiner_reset (GstFlowCombiner * combiner) -{ - GList *iter; - - g_return_if_fail (combiner != NULL); - - GST_DEBUG ("%p reset flow returns", combiner); - - for (iter = combiner->pads.head; iter; iter = iter->next) { - GST_PAD_LAST_FLOW_RETURN (iter->data) = GST_FLOW_OK; - } - - combiner->last_ret = GST_FLOW_OK; -} - -static GstFlowReturn -gst_flow_combiner_get_flow (GstFlowCombiner * combiner) -{ - GstFlowReturn cret = GST_FLOW_OK; - gboolean all_eos = TRUE; - gboolean all_notlinked = TRUE; - GList *iter; - - GST_DEBUG ("%p Combining flow returns", combiner); - - for (iter = combiner->pads.head; iter; iter = iter->next) { - GstFlowReturn fret = GST_PAD_LAST_FLOW_RETURN (iter->data); - - GST_TRACE ("%p pad %" GST_PTR_FORMAT " has flow return of %s (%d)", - combiner, iter->data, gst_flow_get_name (fret), fret); - - if (fret <= GST_FLOW_NOT_NEGOTIATED || fret == GST_FLOW_FLUSHING) { - GST_DEBUG ("%p Error flow return found, returning", combiner); - cret = fret; - goto done; - } - - if (fret != GST_FLOW_NOT_LINKED) { - all_notlinked = FALSE; - if (fret != GST_FLOW_EOS) - all_eos = FALSE; - } - } - if (all_notlinked) - cret = GST_FLOW_NOT_LINKED; - else if (all_eos) - cret = GST_FLOW_EOS; - -done: - GST_DEBUG ("%p Combined flow return: %s (%d)", combiner, - gst_flow_get_name (cret), cret); - return cret; -} - -/** - * gst_flow_combiner_update_flow: - * @combiner: the #GstFlowCombiner - * @fret: the latest #GstFlowReturn received for a pad in this #GstFlowCombiner - * - * Computes the combined flow return for the pads in it. - * - * The #GstFlowReturn parameter should be the last flow return update for a pad - * in this #GstFlowCombiner. It will use this value to be able to shortcut some - * combinations and avoid looking over all pads again. e.g. The last combined - * return is the same as the latest obtained #GstFlowReturn. - * - * Returns: The combined #GstFlowReturn - * Since: 1.4 - */ -GstFlowReturn -gst_flow_combiner_update_flow (GstFlowCombiner * combiner, GstFlowReturn fret) -{ - GstFlowReturn ret; - - g_return_val_if_fail (combiner != NULL, GST_FLOW_ERROR); - - GST_DEBUG ("%p updating combiner with flow %s (%d)", combiner, - gst_flow_get_name (fret), fret); - - if (combiner->last_ret == fret) { - return fret; - } - - if (fret <= GST_FLOW_NOT_NEGOTIATED || fret == GST_FLOW_FLUSHING - || !combiner->pads.head) { - ret = fret; - } else { - ret = gst_flow_combiner_get_flow (combiner); - } - combiner->last_ret = ret; - return ret; -} - -/** - * gst_flow_combiner_update_pad_flow: - * @combiner: the #GstFlowCombiner - * @pad: the #GstPad whose #GstFlowReturn to update - * @fret: the latest #GstFlowReturn received for a pad in this #GstFlowCombiner - * - * Sets the provided pad's last flow return to provided value and computes - * the combined flow return for the pads in it. - * - * The #GstFlowReturn parameter should be the last flow return update for a pad - * in this #GstFlowCombiner. It will use this value to be able to shortcut some - * combinations and avoid looking over all pads again. e.g. The last combined - * return is the same as the latest obtained #GstFlowReturn. - * - * Returns: The combined #GstFlowReturn - * Since: 1.6 - */ -GstFlowReturn -gst_flow_combiner_update_pad_flow (GstFlowCombiner * combiner, GstPad * pad, - GstFlowReturn fret) -{ - g_return_val_if_fail (pad != NULL, GST_FLOW_ERROR); - - GST_PAD_LAST_FLOW_RETURN (pad) = fret; - - return gst_flow_combiner_update_flow (combiner, fret); -} - -/** - * gst_flow_combiner_add_pad: - * @combiner: the #GstFlowCombiner - * @pad: (transfer none): the #GstPad that is being added - * - * Adds a new #GstPad to the #GstFlowCombiner. - * - * Since: 1.4 - */ -void -gst_flow_combiner_add_pad (GstFlowCombiner * combiner, GstPad * pad) -{ - g_return_if_fail (combiner != NULL); - g_return_if_fail (pad != NULL); - - g_queue_push_head (&combiner->pads, gst_object_ref (pad)); -} - -/** - * gst_flow_combiner_remove_pad: - * @combiner: the #GstFlowCombiner - * @pad: (transfer none): the #GstPad to remove - * - * Removes a #GstPad from the #GstFlowCombiner. - * - * Since: 1.4 - */ -void -gst_flow_combiner_remove_pad (GstFlowCombiner * combiner, GstPad * pad) -{ - g_return_if_fail (combiner != NULL); - g_return_if_fail (pad != NULL); - - if (g_queue_remove (&combiner->pads, pad)) - gst_object_unref (pad); -} diff --git a/libs/gst/base/gstflowcombiner.h b/libs/gst/base/gstflowcombiner.h deleted file mode 100644 index fa905a93fc..0000000000 --- a/libs/gst/base/gstflowcombiner.h +++ /dev/null @@ -1,82 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2014 Samsung Electronics. All rights reserved. - * Author: Thiago Santos <ts.santos@sisa.samsung.com> - * - * gstflowcombiner.h: utility to combine multiple flow returns into a single one - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - - -#ifndef __GST_FLOW_COMBINER_H__ -#define __GST_FLOW_COMBINER_H__ - -#include <glib.h> -#include <gst/gst.h> -#include <gst/base/base-prelude.h> - -G_BEGIN_DECLS - -#define GST_TYPE_FLOW_COMBINER gst_flow_combiner_get_type() - -/** - * GstFlowCombiner: - * - * Opaque helper structure to aggregate flow returns. - * - * Since: 1.4 - */ -typedef struct _GstFlowCombiner GstFlowCombiner; - -GST_BASE_API -GstFlowCombiner * gst_flow_combiner_new (void); - -GST_BASE_API -GstFlowCombiner * gst_flow_combiner_ref (GstFlowCombiner * combiner); - -GST_BASE_API -void gst_flow_combiner_unref (GstFlowCombiner * combiner); - -GST_BASE_API -void gst_flow_combiner_free (GstFlowCombiner * combiner); - -GST_BASE_API -GstFlowReturn gst_flow_combiner_update_flow (GstFlowCombiner * combiner, GstFlowReturn fret); - -GST_BASE_API -GstFlowReturn gst_flow_combiner_update_pad_flow (GstFlowCombiner * combiner, GstPad * pad, - GstFlowReturn fret); -GST_BASE_API -void gst_flow_combiner_add_pad (GstFlowCombiner * combiner, GstPad * pad); - -GST_BASE_API -void gst_flow_combiner_remove_pad (GstFlowCombiner * combiner, GstPad * pad); - -GST_BASE_API -void gst_flow_combiner_clear (GstFlowCombiner * combiner); - -GST_BASE_API -void gst_flow_combiner_reset (GstFlowCombiner * combiner); - -GST_BASE_API -GType gst_flow_combiner_get_type (void); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstFlowCombiner, gst_flow_combiner_free) - -G_END_DECLS - -#endif /* __GST_FLOW_COMBINER_H__ */ diff --git a/libs/gst/base/gstindex.c b/libs/gst/base/gstindex.c deleted file mode 100644 index 964508e0ac..0000000000 --- a/libs/gst/base/gstindex.c +++ /dev/null @@ -1,1003 +0,0 @@ -/* GStreamer - * Copyright (C) 2001 RidgeRun (http://www.ridgerun.com/) - * Written by Erik Walthinsen <omega@ridgerun.com> - * - * gstindex.c: Index for mappings and other data - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * SECTION:gstindex - * @title: GstIndexEntry - * @short_description: Generate indexes on objects - * @see_also: #GstIndexFactory - * - * #GstIndex is used to generate a stream index of one or more elements - * in a pipeline. - * - * Elements will overload the set_index and get_index virtual methods in - * #GstElement. When streaming data, the element will add index entries if it - * has an index set. - * - * Each element that adds to the index will do that using a writer_id. The - * writer_id is obtained from gst_index_get_writer_id(). - * - * The application that wants to index the stream will create a new index object - * using gst_index_new() or gst_index_factory_make(). The index is assigned to a - * specific element, a bin or the whole pipeline. This will cause indexable - * elements to add entries to the index while playing. - */ - -/* FIXME: complete gobject annotations */ -/* FIXME-0.11: cleanup API - * - no one seems to use GstIndexGroup, GstIndexCertainty - * - * - the API for application to use the index is mostly missing - * - apps need to get a list of writers - * - apps need to be able to iterate over each writers index entry collection - * - gst_index_get_assoc_entry() should pass ownership - * - the GstIndexEntry structure is large and contains repetitive information - * - we want to allow Indexers to implement a saner storage and create - * GstIndexEntries on demand (the app has to free them), might even make - * sense to ask the app to provide a ptr and fill it. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <gst/gst.h> -#include "gst/glib-compat-private.h" - -/* Index signals and args */ -enum -{ - ENTRY_ADDED, - LAST_SIGNAL -}; - -enum -{ - ARG_0, - ARG_RESOLVER - /* FILL ME */ -}; - -#if 0 -GST_DEBUG_CATEGORY_STATIC (index_debug); -#define GST_CAT_DEFAULT index_debug -#endif - -static void gst_index_finalize (GObject * object); - -static void gst_index_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_index_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static GstIndexGroup *gst_index_group_new (guint groupnum); -static void gst_index_group_free (GstIndexGroup * group); - -static gboolean gst_index_path_resolver (GstIndex * index, GstObject * writer, - gchar ** writer_string, gpointer data); -static gboolean gst_index_gtype_resolver (GstIndex * index, GstObject * writer, - gchar ** writer_string, gpointer data); -static void gst_index_add_entry (GstIndex * index, GstIndexEntry * entry); - -static guint gst_index_signals[LAST_SIGNAL] = { 0 }; - -typedef struct -{ - GstIndexResolverMethod method; - GstIndexResolver resolver; - gpointer user_data; -} -ResolverEntry; - -static const ResolverEntry resolvers[] = { - {GST_INDEX_RESOLVER_CUSTOM, NULL, NULL}, - {GST_INDEX_RESOLVER_GTYPE, gst_index_gtype_resolver, NULL}, - {GST_INDEX_RESOLVER_PATH, gst_index_path_resolver, NULL}, -}; - -#define GST_TYPE_INDEX_RESOLVER (gst_index_resolver_get_type()) -static GType -gst_index_resolver_get_type (void) -{ - static GType index_resolver_type = 0; - static const GEnumValue index_resolver[] = { - {GST_INDEX_RESOLVER_CUSTOM, "GST_INDEX_RESOLVER_CUSTOM", "custom"}, - {GST_INDEX_RESOLVER_GTYPE, "GST_INDEX_RESOLVER_GTYPE", "gtype"}, - {GST_INDEX_RESOLVER_PATH, "GST_INDEX_RESOLVER_PATH", "path"}, - {0, NULL, NULL}, - }; - - if (!index_resolver_type) { - index_resolver_type = - g_enum_register_static ("GstIndexResolver", index_resolver); - } - return index_resolver_type; -} - -G_DEFINE_BOXED_TYPE (GstIndexEntry, gst_index_entry, - (GBoxedCopyFunc) gst_index_entry_copy, - (GBoxedFreeFunc) gst_index_entry_free); - -#if 0 -#define _do_init \ -{ \ - GST_DEBUG_CATEGORY_INIT (index_debug, "GST_INDEX", GST_DEBUG_BOLD, \ - "Generic indexing support"); \ -} -#endif - -G_DEFINE_TYPE (GstIndex, gst_index, GST_TYPE_OBJECT); - -static void -gst_index_class_init (GstIndexClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - /** - * GstIndex::entry-added - * @gstindex: the object which received the signal. - * @arg1: The entry added to the index. - * - * Is emitted when a new entry is added to the index. - */ - gst_index_signals[ENTRY_ADDED] = - g_signal_new ("entry-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstIndexClass, entry_added), NULL, NULL, - NULL, G_TYPE_NONE, 1, GST_TYPE_INDEX_ENTRY); - - gobject_class->set_property = gst_index_set_property; - gobject_class->get_property = gst_index_get_property; - gobject_class->finalize = gst_index_finalize; - - g_object_class_install_property (gobject_class, ARG_RESOLVER, - g_param_spec_enum ("resolver", "Resolver", - "Select a predefined object to string mapper", - GST_TYPE_INDEX_RESOLVER, GST_INDEX_RESOLVER_PATH, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -} - -static void -gst_index_init (GstIndex * index) -{ - index->curgroup = gst_index_group_new (0); - index->maxgroup = 0; - index->groups = g_list_prepend (NULL, index->curgroup); - - index->writers = g_hash_table_new (NULL, NULL); - index->last_id = 0; - - index->method = GST_INDEX_RESOLVER_PATH; - index->resolver = resolvers[index->method].resolver; - index->resolver_user_data = resolvers[index->method].user_data; - - GST_OBJECT_FLAG_SET (index, GST_INDEX_WRITABLE); - GST_OBJECT_FLAG_SET (index, GST_INDEX_READABLE); - - GST_DEBUG ("created new index"); -} - -static void -gst_index_free_writer (gpointer key, gpointer value, gpointer user_data) -{ - GstIndexEntry *entry = (GstIndexEntry *) value; - - if (entry) { - gst_index_entry_free (entry); - } -} - -static void -gst_index_finalize (GObject * object) -{ - GstIndex *index = GST_INDEX (object); - - if (index->groups) { - g_list_foreach (index->groups, (GFunc) gst_index_group_free, NULL); - g_list_free (index->groups); - index->groups = NULL; - } - - if (index->writers) { - g_hash_table_foreach (index->writers, gst_index_free_writer, NULL); - g_hash_table_destroy (index->writers); - index->writers = NULL; - } - - if (index->filter_user_data && index->filter_user_data_destroy) - index->filter_user_data_destroy (index->filter_user_data); - - if (index->resolver_user_data && index->resolver_user_data_destroy) - index->resolver_user_data_destroy (index->resolver_user_data); - - G_OBJECT_CLASS (gst_index_parent_class)->finalize (object); -} - -static void -gst_index_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstIndex *index; - - index = GST_INDEX (object); - - switch (prop_id) { - case ARG_RESOLVER: - index->method = (GstIndexResolverMethod) g_value_get_enum (value); - index->resolver = resolvers[index->method].resolver; - index->resolver_user_data = resolvers[index->method].user_data; - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_index_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstIndex *index; - - index = GST_INDEX (object); - - switch (prop_id) { - case ARG_RESOLVER: - g_value_set_enum (value, index->method); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static GstIndexGroup * -gst_index_group_new (guint groupnum) -{ - GstIndexGroup *indexgroup = g_slice_new (GstIndexGroup); - - indexgroup->groupnum = groupnum; - indexgroup->entries = NULL; - indexgroup->certainty = GST_INDEX_UNKNOWN; - indexgroup->peergroup = -1; - - GST_DEBUG ("created new index group %d", groupnum); - - return indexgroup; -} - -static void -gst_index_group_free (GstIndexGroup * group) -{ - g_slice_free (GstIndexGroup, group); -} - -/* do not resurrect this, add a derived dummy index class instead */ -#if 0 -/** - * gst_index_new: - * - * Create a new dummy index object. Use gst_element_set_index() to assign that - * to an element or pipeline. This index is not storing anything, but will - * still emit e.g. the #GstIndex::entry-added signal. - * - * Returns: (transfer full): a new index object - */ -GstIndex * -gst_index_new (void) -{ - GstIndex *index; - - index = g_object_new (gst_index_get_type (), NULL); - - return index; -} - -/** - * gst_index_commit: - * @index: the index to commit - * @id: the writer that committed the index - * - * Tell the index that the writer with the given id is done - * with this index and is not going to write any more entries - * to it. - */ -void -gst_index_commit (GstIndex * index, gint id) -{ - GstIndexClass *iclass; - - iclass = GST_INDEX_GET_CLASS (index); - - if (iclass->commit) - iclass->commit (index, id); -} - -/** - * gst_index_get_group: - * @index: the index to get the current group from - * - * Get the id of the current group. - * - * Returns: the id of the current group. - */ -gint -gst_index_get_group (GstIndex * index) -{ - return index->curgroup->groupnum; -} - -/** - * gst_index_new_group: - * @index: the index to create the new group in - * - * Create a new group for the given index. It will be - * set as the current group. - * - * Returns: the id of the newly created group. - */ -gint -gst_index_new_group (GstIndex * index) -{ - index->curgroup = gst_index_group_new (++index->maxgroup); - index->groups = g_list_append (index->groups, index->curgroup); - GST_DEBUG ("created new group %d in index", index->maxgroup); - return index->maxgroup; -} - -/** - * gst_index_set_group: - * @index: the index to set the new group in - * @groupnum: the groupnumber to set - * - * Set the current groupnumber to the given argument. - * - * Returns: %TRUE if the operation succeeded, %FALSE if the group - * did not exist. - */ -gboolean -gst_index_set_group (GstIndex * index, gint groupnum) -{ - GList *list; - GstIndexGroup *indexgroup; - - /* first check for null change */ - if (groupnum == index->curgroup->groupnum) - return TRUE; - - /* else search for the proper group */ - list = index->groups; - while (list) { - indexgroup = (GstIndexGroup *) (list->data); - list = g_list_next (list); - if (indexgroup->groupnum == groupnum) { - index->curgroup = indexgroup; - GST_DEBUG ("switched to index group %d", indexgroup->groupnum); - return TRUE; - } - } - - /* couldn't find the group in question */ - GST_DEBUG ("couldn't find index group %d", groupnum); - return FALSE; -} -#endif - -#if 0 -/** - * gst_index_set_certainty: - * @index: the index to set the certainty on - * @certainty: the certainty to set - * - * Set the certainty of the given index. - */ -void -gst_index_set_certainty (GstIndex * index, GstIndexCertainty certainty) -{ - index->curgroup->certainty = certainty; -} - -/** - * gst_index_get_certainty: - * @index: the index to get the certainty of - * - * Get the certainty of the given index. - * - * Returns: the certainty of the index. - */ -GstIndexCertainty -gst_index_get_certainty (GstIndex * index) -{ - return index->curgroup->certainty; -} -#endif - -#if 0 -/** - * gst_index_set_filter: - * @index: the index to register the filter on - * @filter: the filter to register - * @user_data: data passed to the filter function - * - * Lets the app register a custom filter function so that - * it can select what entries should be stored in the index. - */ -void -gst_index_set_filter (GstIndex * index, - GstIndexFilter filter, gpointer user_data) -{ - g_return_if_fail (GST_IS_INDEX (index)); - - gst_index_set_filter_full (index, filter, user_data, NULL); -} - -/** - * gst_index_set_filter_full: - * @index: the index to register the filter on - * @filter: the filter to register - * @user_data: data passed to the filter function - * @user_data_destroy: function to call when @user_data is unset - * - * Lets the app register a custom filter function so that - * it can select what entries should be stored in the index. - */ -void -gst_index_set_filter_full (GstIndex * index, - GstIndexFilter filter, gpointer user_data, GDestroyNotify user_data_destroy) -{ - g_return_if_fail (GST_IS_INDEX (index)); - - if (index->filter_user_data && index->filter_user_data_destroy) - index->filter_user_data_destroy (index->filter_user_data); - - index->filter = filter; - index->filter_user_data = user_data; - index->filter_user_data_destroy = user_data_destroy; -} - -/** - * gst_index_set_resolver: - * @index: the index to register the resolver on - * @resolver: the resolver to register - * @user_data: data passed to the resolver function - * - * Lets the app register a custom function to map index - * ids to writer descriptions. - */ -void -gst_index_set_resolver (GstIndex * index, - GstIndexResolver resolver, gpointer user_data) -{ - gst_index_set_resolver_full (index, resolver, user_data, NULL); -} - -/** - * gst_index_set_resolver_full: - * @index: the index to register the resolver on - * @resolver: the resolver to register - * @user_data: data passed to the resolver function - * @user_data_destroy: destroy function for @user_data - * - * Lets the app register a custom function to map index - * ids to writer descriptions. - */ -void -gst_index_set_resolver_full (GstIndex * index, GstIndexResolver resolver, - gpointer user_data, GDestroyNotify user_data_destroy) -{ - g_return_if_fail (GST_IS_INDEX (index)); - - if (index->resolver_user_data && index->resolver_user_data_destroy) - index->resolver_user_data_destroy (index->resolver_user_data); - - index->resolver = resolver; - index->resolver_user_data = user_data; - index->resolver_user_data_destroy = user_data_destroy; - index->method = GST_INDEX_RESOLVER_CUSTOM; -} -#endif - -/** - * gst_index_entry_copy: - * @entry: the entry to copy - * - * Copies an entry and returns the result. - * - * Free-function: gst_index_entry_free - * - * Returns: (transfer full): a newly allocated #GstIndexEntry. - */ -GstIndexEntry * -gst_index_entry_copy (GstIndexEntry * entry) -{ - GstIndexEntry *new_entry = g_slice_new (GstIndexEntry); - - memcpy (new_entry, entry, sizeof (GstIndexEntry)); - return new_entry; -} - -/** - * gst_index_entry_free: - * @entry: (transfer full): the entry to free - * - * Free the memory used by the given entry. - */ -void -gst_index_entry_free (GstIndexEntry * entry) -{ - switch (entry->type) { - case GST_INDEX_ENTRY_ID: - if (entry->data.id.description) { - g_free (entry->data.id.description); - entry->data.id.description = NULL; - } - break; - case GST_INDEX_ENTRY_ASSOCIATION: - if (entry->data.assoc.assocs) { - g_free (entry->data.assoc.assocs); - entry->data.assoc.assocs = NULL; - } - break; - case GST_INDEX_ENTRY_OBJECT: - break; - case GST_INDEX_ENTRY_FORMAT: - break; - } - - g_slice_free (GstIndexEntry, entry); -} - -#if 0 -/** - * gst_index_add_format: - * @index: the index to add the entry to - * @id: the id of the index writer - * @format: the format to add to the index - * - * Adds a format entry into the index. This function is - * used to map dynamic #GstFormat ids to their original - * format key. - * - * Free-function: gst_index_entry_free - * - * Returns: (transfer full): a pointer to the newly added entry in the index. - */ -GstIndexEntry * -gst_index_add_format (GstIndex * index, gint id, GstFormat format) -{ - GstIndexEntry *entry; - const GstFormatDefinition *def; - - g_return_val_if_fail (GST_IS_INDEX (index), NULL); - g_return_val_if_fail (format != 0, NULL); - - if (!GST_INDEX_IS_WRITABLE (index) || id == -1) - return NULL; - - entry = g_slice_new (GstIndexEntry); - entry->type = GST_INDEX_ENTRY_FORMAT; - entry->id = id; - entry->data.format.format = format; - - def = gst_format_get_details (format); - entry->data.format.key = def->nick; - - gst_index_add_entry (index, entry); - - return entry; -} -#endif - -/** - * gst_index_add_id: - * @index: the index to add the entry to - * @id: the id of the index writer - * @description: the description of the index writer - * - * Add an id entry into the index. - * - * Returns: a pointer to the newly added entry in the index. - */ -GstIndexEntry * -gst_index_add_id (GstIndex * index, gint id, gchar * description) -{ - GstIndexEntry *entry; - - g_return_val_if_fail (GST_IS_INDEX (index), NULL); - g_return_val_if_fail (description != NULL, NULL); - - if (!GST_INDEX_IS_WRITABLE (index) || id == -1) - return NULL; - - entry = g_slice_new (GstIndexEntry); - entry->type = GST_INDEX_ENTRY_ID; - entry->id = id; - entry->data.id.description = description; - - gst_index_add_entry (index, entry); - - return entry; -} - -static gboolean -gst_index_path_resolver (GstIndex * index, GstObject * writer, - gchar ** writer_string, gpointer data) -{ - *writer_string = gst_object_get_path_string (writer); - - return TRUE; -} - -static gboolean -gst_index_gtype_resolver (GstIndex * index, GstObject * writer, - gchar ** writer_string, gpointer data) -{ - g_return_val_if_fail (writer != NULL, FALSE); - - if (GST_IS_PAD (writer)) { - GstObject *element = gst_object_get_parent (GST_OBJECT (writer)); - gchar *name; - - name = gst_object_get_name (writer); - if (element) { - *writer_string = g_strdup_printf ("%s.%s", - G_OBJECT_TYPE_NAME (element), name); - gst_object_unref (element); - } else { - *writer_string = name; - name = NULL; - } - - g_free (name); - - } else { - *writer_string = g_strdup (G_OBJECT_TYPE_NAME (writer)); - } - - return TRUE; -} - -/** - * gst_index_get_writer_id: - * @index: the index to get a unique write id for - * @writer: the #GstObject to allocate an id for - * @id: a pointer to a gint to hold the id - * - * Before entries can be added to the index, a writer - * should obtain a unique id. The methods to add new entries - * to the index require this id as an argument. - * - * The application can implement a custom function to map the writer object - * to a string. That string will be used to register or look up an id - * in the index. - * - * > The caller must not hold @writer's GST_OBJECT_LOCK(), as the default - * > resolver may call functions that take the object lock as well, and - * > the lock is not recursive. - * - * Returns: %TRUE if the writer would be mapped to an id. - */ -gboolean -gst_index_get_writer_id (GstIndex * index, GstObject * writer, gint * id) -{ - gchar *writer_string = NULL; - GstIndexEntry *entry; - GstIndexClass *iclass; - gboolean success = FALSE; - - g_return_val_if_fail (GST_IS_INDEX (index), FALSE); - g_return_val_if_fail (GST_IS_OBJECT (writer), FALSE); - g_return_val_if_fail (id, FALSE); - - *id = -1; - - /* first try to get a previously cached id */ - entry = g_hash_table_lookup (index->writers, writer); - if (entry == NULL) { - - iclass = GST_INDEX_GET_CLASS (index); - - /* let the app make a string */ - if (index->resolver) { - gboolean res; - - res = - index->resolver (index, writer, &writer_string, - index->resolver_user_data); - if (!res) - return FALSE; - } else { - g_warning ("no resolver found"); - return FALSE; - } - - /* if the index has a resolver, make it map this string to an id */ - if (iclass->get_writer_id) { - success = iclass->get_writer_id (index, id, writer_string); - } - /* if the index could not resolve, we allocate one ourselves */ - if (!success) { - *id = ++index->last_id; - } - - entry = gst_index_add_id (index, *id, writer_string); - if (!entry) { - /* index is probably not writable, make an entry anyway - * to keep it in our cache */ - entry = g_slice_new (GstIndexEntry); - entry->type = GST_INDEX_ENTRY_ID; - entry->id = *id; - entry->data.id.description = writer_string; - } - g_hash_table_insert (index->writers, writer, entry); - } else { - *id = entry->id; - } - - return TRUE; -} - -static void -gst_index_add_entry (GstIndex * index, GstIndexEntry * entry) -{ - GstIndexClass *iclass; - - iclass = GST_INDEX_GET_CLASS (index); - - if (iclass->add_entry) { - iclass->add_entry (index, entry); - } - - g_signal_emit (index, gst_index_signals[ENTRY_ADDED], 0, entry); -} - -/** - * gst_index_add_associationv: - * @index: the index to add the entry to - * @id: the id of the index writer - * @flags: optional flags for this entry - * @n: number of associations - * @list: (array length=n): list of associations - * - * Associate given format/value pairs with each other. - * - * Returns: a pointer to the newly added entry in the index. - */ -GstIndexEntry * -gst_index_add_associationv (GstIndex * index, gint id, - GstIndexAssociationFlags flags, gint n, const GstIndexAssociation * list) -{ - GstIndexEntry *entry; - - g_return_val_if_fail (n > 0, NULL); - g_return_val_if_fail (list != NULL, NULL); - g_return_val_if_fail (GST_IS_INDEX (index), NULL); - - if (!GST_INDEX_IS_WRITABLE (index) || id == -1) - return NULL; - - entry = g_slice_new (GstIndexEntry); - - entry->type = GST_INDEX_ENTRY_ASSOCIATION; - entry->id = id; - entry->data.assoc.flags = flags; - entry->data.assoc.assocs = g_memdup2 (list, sizeof (GstIndexAssociation) * n); - entry->data.assoc.nassocs = n; - - gst_index_add_entry (index, entry); - - return entry; -} - -#if 0 -/** - * gst_index_add_association: - * @index: the index to add the entry to - * @id: the id of the index writer - * @flags: optional flags for this entry - * @format: the format of the value - * @value: the value - * @...: other format/value pairs or 0 to end the list - * - * Associate given format/value pairs with each other. - * Be sure to pass gint64 values to this functions varargs, - * you might want to use a gint64 cast to be sure. - * - * Returns: a pointer to the newly added entry in the index. - */ -GstIndexEntry * -gst_index_add_association (GstIndex * index, gint id, - GstIndexAssociationFlags flags, GstFormat format, gint64 value, ...) -{ - va_list args; - GstIndexEntry *entry; - GstIndexAssociation *list; - gint n_assocs = 0; - GstFormat cur_format; - GArray *array; - - g_return_val_if_fail (GST_IS_INDEX (index), NULL); - g_return_val_if_fail (format != 0, NULL); - - if (!GST_INDEX_IS_WRITABLE (index) || id == -1) - return NULL; - - array = g_array_new (FALSE, FALSE, sizeof (GstIndexAssociation)); - - { - GstIndexAssociation a; - - a.format = format; - a.value = value; - n_assocs = 1; - g_array_append_val (array, a); - } - - va_start (args, value); - - while ((cur_format = va_arg (args, GstFormat))) { - GstIndexAssociation a; - - a.format = cur_format; - a.value = va_arg (args, gint64); - n_assocs++; - g_array_append_val (array, a); - } - - va_end (args); - - list = (GstIndexAssociation *) g_array_free (array, FALSE); - - entry = gst_index_add_associationv (index, id, flags, n_assocs, list); - g_free (list); - - return entry; -} - -/** - * gst_index_add_object: - * @index: the index to add the object to - * @id: the id of the index writer - * @key: a key for the object - * @type: the GType of the object - * @object: a pointer to the object to add - * - * Add the given object to the index with the given key. - * - * This function is not yet implemented. - * - * Returns: a pointer to the newly added entry in the index. - */ -GstIndexEntry * -gst_index_add_object (GstIndex * index, gint id, gchar * key, - GType type, gpointer object) -{ - if (!GST_INDEX_IS_WRITABLE (index) || id == -1) - return NULL; - - return NULL; -} -#endif - -static gint -gst_index_compare_func (gconstpointer a, gconstpointer b, gpointer user_data) -{ - if (a < b) - return -1; - if (a > b) - return 1; - return 0; -} - -/** - * gst_index_get_assoc_entry: - * @index: the index to search - * @id: the id of the index writer - * @method: The lookup method to use - * @flags: Flags for the entry - * @format: the format of the value - * @value: the value to find - * - * Finds the given format/value in the index - * - * Returns: (nullable): the entry associated with the value or %NULL if the - * value was not found. - */ -GstIndexEntry * -gst_index_get_assoc_entry (GstIndex * index, gint id, - GstIndexLookupMethod method, GstIndexAssociationFlags flags, - GstFormat format, gint64 value) -{ - g_return_val_if_fail (GST_IS_INDEX (index), NULL); - - if (id == -1) - return NULL; - - return gst_index_get_assoc_entry_full (index, id, method, flags, format, - value, gst_index_compare_func, NULL); -} - -/** - * gst_index_get_assoc_entry_full: - * @index: the index to search - * @id: the id of the index writer - * @method: The lookup method to use - * @flags: Flags for the entry - * @format: the format of the value - * @value: the value to find - * @func: the function used to compare entries - * @user_data: user data passed to the compare function - * - * Finds the given format/value in the index with the given - * compare function and user_data. - * - * Returns: (nullable): the entry associated with the value or %NULL if the - * value was not found. - */ -GstIndexEntry * -gst_index_get_assoc_entry_full (GstIndex * index, gint id, - GstIndexLookupMethod method, GstIndexAssociationFlags flags, - GstFormat format, gint64 value, GCompareDataFunc func, gpointer user_data) -{ - GstIndexClass *iclass; - - g_return_val_if_fail (GST_IS_INDEX (index), NULL); - - if (id == -1) - return NULL; - - iclass = GST_INDEX_GET_CLASS (index); - - if (iclass->get_assoc_entry) - return iclass->get_assoc_entry (index, id, method, flags, format, value, - func, user_data); - - return NULL; -} - -/** - * gst_index_entry_assoc_map: - * @entry: the index to search - * @format: the format of the value the find - * @value: a pointer to store the value - * - * Gets alternative formats associated with the indexentry. - * - * Returns: %TRUE if there was a value associated with the given - * format. - */ -gboolean -gst_index_entry_assoc_map (GstIndexEntry * entry, - GstFormat format, gint64 * value) -{ - gint i; - - g_return_val_if_fail (entry != NULL, FALSE); - g_return_val_if_fail (value != NULL, FALSE); - - for (i = 0; i < GST_INDEX_NASSOCS (entry); i++) { - if (GST_INDEX_ASSOC_FORMAT (entry, i) == format) { - *value = GST_INDEX_ASSOC_VALUE (entry, i); - return TRUE; - } - } - return FALSE; -} diff --git a/libs/gst/base/gstindex.h b/libs/gst/base/gstindex.h deleted file mode 100644 index 476146574b..0000000000 --- a/libs/gst/base/gstindex.h +++ /dev/null @@ -1,449 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> - * 2000 Wim Taymans <wim.taymans@chello.be> - * - * gstindex.h: Header for GstIndex, base class to handle efficient - * storage or caching of seeking information. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_INDEX_H__ -#define __GST_INDEX_H__ - -#include <gst/gstobject.h> -#include <gst/gstformat.h> -#include <gst/gstpluginfeature.h> -#include <gst/base/base-prelude.h> - -G_BEGIN_DECLS - -#define GST_TYPE_INDEX (gst_index_get_type ()) -#define GST_INDEX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_INDEX, GstIndex)) -#define GST_IS_INDEX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_INDEX)) -#define GST_INDEX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_INDEX, GstIndexClass)) -#define GST_IS_INDEX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_INDEX)) -#define GST_INDEX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_INDEX, GstIndexClass)) - -#define GST_TYPE_INDEX_ENTRY (gst_index_entry_get_type()) - -typedef struct _GstIndexEntry GstIndexEntry; -typedef struct _GstIndexGroup GstIndexGroup; -typedef struct _GstIndex GstIndex; -typedef struct _GstIndexClass GstIndexClass; - -/** - * GstIndexCertainty: - * @GST_INDEX_UNKNOWN: accuracy is not known - * @GST_INDEX_CERTAIN: accuracy is perfect - * @GST_INDEX_FUZZY: accuracy is fuzzy - * - * The certainty of a group in the index. - */ -typedef enum { - GST_INDEX_UNKNOWN, - GST_INDEX_CERTAIN, - GST_INDEX_FUZZY -} GstIndexCertainty; - -/** - * GstIndexEntryType: - * @GST_INDEX_ENTRY_ID: This entry is an id that maps an index id to its owner object - * @GST_INDEX_ENTRY_ASSOCIATION: This entry is an association between formats - * @GST_INDEX_ENTRY_OBJECT: An object - * @GST_INDEX_ENTRY_FORMAT: A format definition - * - * The different types of entries in the index. - */ -typedef enum { - GST_INDEX_ENTRY_ID, - GST_INDEX_ENTRY_ASSOCIATION, - GST_INDEX_ENTRY_OBJECT, - GST_INDEX_ENTRY_FORMAT -} GstIndexEntryType; - -/** - * GstIndexLookupMethod: - * @GST_INDEX_LOOKUP_EXACT: There has to be an exact indexentry with the given format/value - * @GST_INDEX_LOOKUP_BEFORE: The exact entry or the one before it - * @GST_INDEX_LOOKUP_AFTER: The exact entry or the one after it - * - * Specify the method to find an index entry in the index. - */ -typedef enum { - GST_INDEX_LOOKUP_EXACT, - GST_INDEX_LOOKUP_BEFORE, - GST_INDEX_LOOKUP_AFTER -} GstIndexLookupMethod; - -/** - * GST_INDEX_NASSOCS: - * @entry: The entry to query - * - * Get the number of associations in the entry. - */ -#define GST_INDEX_NASSOCS(entry) ((entry)->data.assoc.nassocs) - -/** - * GST_INDEX_ASSOC_FLAGS: - * @entry: The entry to query - * - * Get the flags for this entry. - */ -#define GST_INDEX_ASSOC_FLAGS(entry) ((entry)->data.assoc.flags) - -/** - * GST_INDEX_ASSOC_FORMAT: - * @entry: The entry to query - * @i: The format index - * - * Get the i-th format of the entry. - */ -#define GST_INDEX_ASSOC_FORMAT(entry,i) ((entry)->data.assoc.assocs[(i)].format) - -/** - * GST_INDEX_ASSOC_VALUE: - * @entry: The entry to query - * @i: The value index - * - * Get the i-th value of the entry. - */ -#define GST_INDEX_ASSOC_VALUE(entry,i) ((entry)->data.assoc.assocs[(i)].value) - -typedef struct _GstIndexAssociation GstIndexAssociation; - -/** - * GstIndexAssociation: - * @format: the format of the association - * @value: the value of the association - * - * An association in an entry. - */ -struct _GstIndexAssociation { - GstFormat format; - gint64 value; -}; - -/** - * GstIndexAssociationFlags: - * @GST_INDEX_ASSOCIATION_FLAG_NONE: no extra flags - * @GST_INDEX_ASSOCIATION_FLAG_KEY_UNIT: the entry marks a key unit, a key unit is one - * that marks a place where one can randomly seek to. - * @GST_INDEX_ASSOCIATION_FLAG_DELTA_UNIT: the entry marks a delta unit, a delta unit - * is one that marks a place where one can relatively seek to. - * @GST_INDEX_ASSOCIATION_FLAG_LAST: extra user defined flags should start here. - * - * Flags for an association entry. - */ -typedef enum { - GST_INDEX_ASSOCIATION_FLAG_NONE = 0, - GST_INDEX_ASSOCIATION_FLAG_KEY_UNIT = (1 << 0), - GST_INDEX_ASSOCIATION_FLAG_DELTA_UNIT = (1 << 1), - - /* new flags should start here */ - GST_INDEX_ASSOCIATION_FLAG_LAST = (1 << 8) -} GstIndexAssociationFlags; - -/** - * GST_INDEX_FORMAT_FORMAT: - * @entry: The entry to query - * - * Get the format of the format entry - */ -#define GST_INDEX_FORMAT_FORMAT(entry) ((entry)->data.format.format) - -/** - * GST_INDEX_FORMAT_KEY: - * @entry: The entry to query - * - * Get the key of the format entry - */ -#define GST_INDEX_FORMAT_KEY(entry) ((entry)->data.format.key) - -/** - * GST_INDEX_ID_INVALID: - * - * Constant for an invalid index id - */ -#define GST_INDEX_ID_INVALID (-1) - -/** - * GST_INDEX_ID_DESCRIPTION: - * @entry: The entry to query - * - * Get the description of the id entry - */ -#define GST_INDEX_ID_DESCRIPTION(entry) ((entry)->data.id.description) - -/** - * GstIndexEntry: - * - * The basic element of an index. - */ -struct _GstIndexEntry { - /*< private >*/ - GstIndexEntryType type; - gint id; - - union { - struct { - gchar *description; - } id; - struct { - gint nassocs; - GstIndexAssociation - *assocs; - GstIndexAssociationFlags flags; - } assoc; - struct { - gchar *key; - GType type; - gpointer object; - } object; - struct { - GstFormat format; - const gchar *key; - } format; - } data; - // FIXME 2.0: add padding -}; - -/** - * GstIndexGroup: - * - * A group of related entries in an index. - */ - -struct _GstIndexGroup { - /*< private >*/ - /* unique ID of group in index */ - gint groupnum; - - /* list of entries */ - GList *entries; - - /* the certainty level of the group */ - GstIndexCertainty certainty; - - /* peer group that contains more certain entries */ - gint peergroup; - // FIXME 2.0: add padding -}; - -/** - * GstIndexFilter: - * @index: The index being queried - * @entry: The entry to be added. - * @user_data: User data passed to the function. - * - * Function to filter out entries in the index. - * - * Returns: This function should return %TRUE if the entry is to be added - * to the index, %FALSE otherwise. - * - */ -typedef gboolean (*GstIndexFilter) (GstIndex *index, - GstIndexEntry *entry, - gpointer user_data); -/** - * GstIndexResolverMethod: - * @GST_INDEX_RESOLVER_CUSTOM: Use a custom resolver - * @GST_INDEX_RESOLVER_GTYPE: Resolve based on the GType of the object - * @GST_INDEX_RESOLVER_PATH: Resolve on the path in graph - * - * The method used to resolve index writers - */ -typedef enum { - GST_INDEX_RESOLVER_CUSTOM, - GST_INDEX_RESOLVER_GTYPE, - GST_INDEX_RESOLVER_PATH -} GstIndexResolverMethod; - -/** - * GstIndexResolver: - * @index: the index being queried. - * @writer: The object that wants to write - * @writer_string: A description of the writer. - * @user_data: user_data as registered - * - * Function to resolve ids to writer descriptions. - * - * Returns: %TRUE if an id could be assigned to the writer. - */ -typedef gboolean (*GstIndexResolver) (GstIndex *index, - GstObject *writer, - gchar **writer_string, - gpointer user_data); - -/** - * GstIndexFlags: - * @GST_INDEX_WRITABLE: The index is writable - * @GST_INDEX_READABLE: The index is readable - * @GST_INDEX_FLAG_LAST: First flag that can be used by subclasses - * - * Flags for this index - */ -typedef enum { - GST_INDEX_WRITABLE = (GST_OBJECT_FLAG_LAST << 0), - GST_INDEX_READABLE = (GST_OBJECT_FLAG_LAST << 1), - - GST_INDEX_FLAG_LAST = (GST_OBJECT_FLAG_LAST << 8) -} GstIndexFlags; - -/** - * GST_INDEX_IS_READABLE: - * @obj: The index to check - * - * Check if the index can be read from - */ -#define GST_INDEX_IS_READABLE(obj) (GST_OBJECT_FLAG_IS_SET (obj, GST_INDEX_READABLE)) - -/** - * GST_INDEX_IS_WRITABLE: - * @obj: The index to check - * - * Check if the index can be written to - */ -#define GST_INDEX_IS_WRITABLE(obj) (GST_OBJECT_FLAG_IS_SET (obj, GST_INDEX_WRITABLE)) - -/** - * GstIndex: - * - * Opaque #GstIndex structure. - */ -struct _GstIndex { - GstObject object; - - /*< private >*/ - GList *groups; - GstIndexGroup *curgroup; - gint maxgroup; - - GstIndexResolverMethod method; - GstIndexResolver resolver; - gpointer resolver_user_data; - GDestroyNotify resolver_user_data_destroy; - - GstIndexFilter filter; - gpointer filter_user_data; - GDestroyNotify filter_user_data_destroy; - - GHashTable *writers; - gint last_id; - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; -}; - -struct _GstIndexClass { - GstObjectClass parent_class; - - /*< protected >*/ - gboolean (*get_writer_id) (GstIndex *index, gint *id, gchar *writer); - - void (*commit) (GstIndex *index, gint id); - - /* abstract methods */ - void (*add_entry) (GstIndex *index, GstIndexEntry *entry); - - GstIndexEntry* (*get_assoc_entry) (GstIndex *index, gint id, - GstIndexLookupMethod method, GstIndexAssociationFlags flags, - GstFormat format, gint64 value, - GCompareDataFunc func, - gpointer user_data); - /* signals */ - void (*entry_added) (GstIndex *index, GstIndexEntry *entry); - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; -}; - -static -GType gst_index_get_type (void); - -#if 0 -GstIndex* gst_index_new (void); -void gst_index_commit (GstIndex *index, gint id); - -gint gst_index_get_group (GstIndex *index); -gint gst_index_new_group (GstIndex *index); -gboolean gst_index_set_group (GstIndex *index, gint groupnum); - -void gst_index_set_certainty (GstIndex *index, - GstIndexCertainty certainty); -GstIndexCertainty gst_index_get_certainty (GstIndex *index); - -static -void gst_index_set_filter (GstIndex *index, - GstIndexFilter filter, gpointer user_data); -static -void gst_index_set_filter_full (GstIndex *index, - GstIndexFilter filter, gpointer user_data, - GDestroyNotify user_data_destroy); - -void gst_index_set_resolver (GstIndex *index, - GstIndexResolver resolver, gpointer user_data); -void gst_index_set_resolver_full (GstIndex *index, GstIndexResolver resolver, - gpointer user_data, - GDestroyNotify user_data_destroy); -#endif - -static -gboolean gst_index_get_writer_id (GstIndex *index, GstObject *writer, gint *id); - -#if 0 -GstIndexEntry* gst_index_add_format (GstIndex *index, gint id, GstFormat format); -#endif - -static -GstIndexEntry* gst_index_add_associationv (GstIndex * index, gint id, GstIndexAssociationFlags flags, - gint n, const GstIndexAssociation * list); -#if 0 -GstIndexEntry* gst_index_add_association (GstIndex *index, gint id, GstIndexAssociationFlags flags, - GstFormat format, gint64 value, ...) -GstIndexEntry* gst_index_add_object (GstIndex *index, gint id, gchar *key, - GType type, gpointer object); -#endif - -static -GstIndexEntry* gst_index_add_id (GstIndex *index, gint id, - gchar *description); - -static -GstIndexEntry* gst_index_get_assoc_entry (GstIndex *index, gint id, - GstIndexLookupMethod method, GstIndexAssociationFlags flags, - GstFormat format, gint64 value); -static -GstIndexEntry* gst_index_get_assoc_entry_full (GstIndex *index, gint id, - GstIndexLookupMethod method, GstIndexAssociationFlags flags, - GstFormat format, gint64 value, - GCompareDataFunc func, - gpointer user_data); - -/* working with index entries */ -static -GType gst_index_entry_get_type (void); -static -GstIndexEntry * gst_index_entry_copy (GstIndexEntry *entry); -static -void gst_index_entry_free (GstIndexEntry *entry); -static -gboolean gst_index_entry_assoc_map (GstIndexEntry *entry, - GstFormat format, gint64 *value); - -G_END_DECLS - -#endif /* __GST_INDEX_H__ */ diff --git a/libs/gst/base/gstmemindex.c b/libs/gst/base/gstmemindex.c deleted file mode 100644 index b667447523..0000000000 --- a/libs/gst/base/gstmemindex.c +++ /dev/null @@ -1,430 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include <gst/gst.h> - -#define GST_TYPE_MEM_INDEX \ - (gst_index_get_type ()) -#define GST_MEM_INDEX(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MEM_INDEX, GstMemIndex)) -#define GST_MEM_INDEX_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_MEM_INDEX, GstMemIndexClass)) -#define GST_IS_MEM_INDEX(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MEM_INDEX)) -#define GST_IS_MEM_INDEX_CLASS(klass) \ - (GST_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MEM_INDEX)) - -/* - * Object model: - * - * All entries are simply added to a GList first. Then we build - * an index to each entry for each id/format - * - * - * memindex - * -----------------------------... - * ! ! - * id1 id2 - * ------------ - * ! ! - * format1 format2 - * ! ! - * GTree GTree - * - * - * The memindex creates a MemIndexId object for each writer id, a - * Hashtable is kept to map the id to the MemIndexId - * - * The MemIndexId keeps a MemIndexFormatIndex for each format the - * specific writer wants indexed. - * - * The MemIndexFormatIndex keeps all the values of the particular - * format in a GTree, The values of the GTree point back to the entry. - * - * Finding a value for an id/format requires locating the correct GTree, - * then do a lookup in the Tree to get the required value. - */ - -typedef struct -{ - GstFormat format; - gint offset; - GTree *tree; -} -GstMemIndexFormatIndex; - -typedef struct -{ - gint id; - GHashTable *format_index; -} -GstMemIndexId; - -typedef struct _GstMemIndex GstMemIndex; -typedef struct _GstMemIndexClass GstMemIndexClass; - -struct _GstMemIndex -{ - GstIndex parent; - - GList *associations; - - GHashTable *id_index; -}; - -struct _GstMemIndexClass -{ - GstIndexClass parent_class; -}; - -static void gst_mem_index_finalize (GObject * object); - -static void gst_mem_index_add_entry (GstIndex * index, GstIndexEntry * entry); -static GstIndexEntry *gst_mem_index_get_assoc_entry (GstIndex * index, gint id, - GstIndexLookupMethod method, GstIndexAssociationFlags flags, - GstFormat format, gint64 value, GCompareDataFunc func, gpointer user_data); - -#define CLASS(mem_index) GST_MEM_INDEX_CLASS (G_OBJECT_GET_CLASS (mem_index)) - -static GType gst_mem_index_get_type (void); - -G_DEFINE_TYPE (GstMemIndex, gst_mem_index, GST_TYPE_INDEX); - -static void -gst_mem_index_class_init (GstMemIndexClass * klass) -{ - GObjectClass *gobject_class; - GstIndexClass *gstindex_class; - - gobject_class = (GObjectClass *) klass; - gstindex_class = (GstIndexClass *) klass; - - gobject_class->finalize = gst_mem_index_finalize; - - gstindex_class->add_entry = GST_DEBUG_FUNCPTR (gst_mem_index_add_entry); - gstindex_class->get_assoc_entry = - GST_DEBUG_FUNCPTR (gst_mem_index_get_assoc_entry); -} - -static void -gst_mem_index_init (GstMemIndex * index) -{ - GST_DEBUG ("created new mem index"); - - index->associations = NULL; - index->id_index = g_hash_table_new (g_int_hash, g_int_equal); -} - -static void -gst_mem_index_free_format (gpointer key, gpointer value, gpointer user_data) -{ - GstMemIndexFormatIndex *index = (GstMemIndexFormatIndex *) value; - - if (index->tree) { - g_tree_destroy (index->tree); - } - - g_slice_free (GstMemIndexFormatIndex, index); -} - -static void -gst_mem_index_free_id (gpointer key, gpointer value, gpointer user_data) -{ - GstMemIndexId *id_index = (GstMemIndexId *) value; - - if (id_index->format_index) { - g_hash_table_foreach (id_index->format_index, gst_mem_index_free_format, - NULL); - g_hash_table_destroy (id_index->format_index); - id_index->format_index = NULL; - } - - g_slice_free (GstMemIndexId, id_index); -} - -static void -gst_mem_index_finalize (GObject * object) -{ - GstMemIndex *memindex = GST_MEM_INDEX (object); - - /* Delete the trees referencing the associations first */ - if (memindex->id_index) { - g_hash_table_foreach (memindex->id_index, gst_mem_index_free_id, NULL); - g_hash_table_destroy (memindex->id_index); - memindex->id_index = NULL; - } - - /* Then delete the associations themselves */ - if (memindex->associations) { - g_list_foreach (memindex->associations, (GFunc) gst_index_entry_free, NULL); - g_list_free (memindex->associations); - memindex->associations = NULL; - } - - G_OBJECT_CLASS (gst_mem_index_parent_class)->finalize (object); -} - -static void -gst_mem_index_add_id (GstIndex * index, GstIndexEntry * entry) -{ - GstMemIndex *memindex = GST_MEM_INDEX (index); - GstMemIndexId *id_index; - - id_index = g_hash_table_lookup (memindex->id_index, &entry->id); - - if (!id_index) { - id_index = g_slice_new0 (GstMemIndexId); - - id_index->id = entry->id; - id_index->format_index = g_hash_table_new (g_int_hash, g_int_equal); - g_hash_table_insert (memindex->id_index, &id_index->id, id_index); - } -} - -static gint -mem_index_compare (gconstpointer a, gconstpointer b, gpointer user_data) -{ - GstMemIndexFormatIndex *index = user_data; - gint64 val1, val2; - gint64 diff; - - val1 = GST_INDEX_ASSOC_VALUE (((GstIndexEntry *) a), index->offset); - val2 = GST_INDEX_ASSOC_VALUE (((GstIndexEntry *) b), index->offset); - - diff = (val2 - val1); - - return (diff == 0 ? 0 : (diff > 0 ? 1 : -1)); -} - -static void -gst_mem_index_index_format (GstMemIndexId * id_index, GstIndexEntry * entry, - gint assoc) -{ - GstMemIndexFormatIndex *index; - GstFormat *format; - - format = &GST_INDEX_ASSOC_FORMAT (entry, assoc); - - index = g_hash_table_lookup (id_index->format_index, format); - - if (!index) { - index = g_slice_new0 (GstMemIndexFormatIndex); - - index->format = *format; - index->offset = assoc; - index->tree = g_tree_new_with_data (mem_index_compare, index); - - g_hash_table_insert (id_index->format_index, &index->format, index); - } - - g_tree_insert (index->tree, entry, entry); -} - -static void -gst_mem_index_add_association (GstIndex * index, GstIndexEntry * entry) -{ - GstMemIndex *memindex = GST_MEM_INDEX (index); - GstMemIndexId *id_index; - - memindex->associations = g_list_prepend (memindex->associations, entry); - - id_index = g_hash_table_lookup (memindex->id_index, &entry->id); - if (id_index) { - gint i; - - for (i = 0; i < GST_INDEX_NASSOCS (entry); i++) { - gst_mem_index_index_format (id_index, entry, i); - } - } -} - -static void -gst_mem_index_add_object (GstIndex * index, GstIndexEntry * entry) -{ -} - -static void -gst_mem_index_add_format (GstIndex * index, GstIndexEntry * entry) -{ -} - -static void -gst_mem_index_add_entry (GstIndex * index, GstIndexEntry * entry) -{ - GST_LOG_OBJECT (index, "added this entry"); - - switch (entry->type) { - case GST_INDEX_ENTRY_ID: - gst_mem_index_add_id (index, entry); - break; - case GST_INDEX_ENTRY_ASSOCIATION: - gst_mem_index_add_association (index, entry); - break; - case GST_INDEX_ENTRY_OBJECT: - gst_mem_index_add_object (index, entry); - break; - case GST_INDEX_ENTRY_FORMAT: - gst_mem_index_add_format (index, entry); - break; - default: - break; - } -} - -typedef struct -{ - gint64 value; - GstMemIndexFormatIndex *index; - gboolean exact; - GstIndexEntry *lower; - gint64 low_diff; - GstIndexEntry *higher; - gint64 high_diff; -} -GstMemIndexSearchData; - -static gint -mem_index_search (gconstpointer a, gconstpointer b) -{ - GstMemIndexSearchData *data = (GstMemIndexSearchData *) b; - GstMemIndexFormatIndex *index = data->index; - gint64 val1, val2; - gint64 diff; - - val1 = GST_INDEX_ASSOC_VALUE (((GstIndexEntry *) a), index->offset); - val2 = data->value; - - diff = (val1 - val2); - if (diff == 0) - return 0; - - /* exact matching, don't update low/high */ - if (data->exact) - return (diff > 0 ? 1 : -1); - - if (diff < 0) { - if (diff > data->low_diff) { - data->low_diff = diff; - data->lower = (GstIndexEntry *) a; - } - diff = -1; - } else { - if (diff < data->high_diff) { - data->high_diff = diff; - data->higher = (GstIndexEntry *) a; - } - diff = 1; - } - - return diff; -} - -static GstIndexEntry * -gst_mem_index_get_assoc_entry (GstIndex * index, gint id, - GstIndexLookupMethod method, - GstIndexAssociationFlags flags, - GstFormat format, gint64 value, GCompareDataFunc func, gpointer user_data) -{ - GstMemIndex *memindex = GST_MEM_INDEX (index); - GstMemIndexId *id_index; - GstMemIndexFormatIndex *format_index; - GstIndexEntry *entry; - GstMemIndexSearchData data; - - id_index = g_hash_table_lookup (memindex->id_index, &id); - if (!id_index) - return NULL; - - format_index = g_hash_table_lookup (id_index->format_index, &format); - if (!format_index) - return NULL; - - data.value = value; - data.index = format_index; - data.exact = (method == GST_INDEX_LOOKUP_EXACT); - - /* setup data for low/high checks if we are not looking - * for an exact match */ - if (!data.exact) { - data.low_diff = G_MININT64; - data.lower = NULL; - data.high_diff = G_MAXINT64; - data.higher = NULL; - } - - entry = g_tree_search (format_index->tree, mem_index_search, &data); - - /* get the low/high values if we're not exact */ - if (entry == NULL && !data.exact) { - if (method == GST_INDEX_LOOKUP_BEFORE) - entry = data.lower; - else if (method == GST_INDEX_LOOKUP_AFTER) { - entry = data.higher; - } - } - - if (entry && ((GST_INDEX_ASSOC_FLAGS (entry) & flags) != flags)) { - if (method != GST_INDEX_LOOKUP_EXACT) { - GList *l_entry = g_list_find (memindex->associations, entry); - - entry = NULL; - - while (l_entry) { - entry = (GstIndexEntry *) l_entry->data; - - if (entry->id == id && (GST_INDEX_ASSOC_FLAGS (entry) & flags) == flags) - break; - - if (method == GST_INDEX_LOOKUP_BEFORE) - l_entry = g_list_next (l_entry); - else if (method == GST_INDEX_LOOKUP_AFTER) { - l_entry = g_list_previous (l_entry); - } - } - } else { - entry = NULL; - } - } - - return entry; -} - -#if 0 -gboolean -gst_mem_index_plugin_init (GstPlugin * plugin) -{ - GstIndexFactory *factory; - - factory = gst_index_factory_new ("memindex", - "A index that stores entries in memory", gst_mem_index_get_type ()); - - if (factory == NULL) { - g_warning ("failed to create memindex factory"); - return FALSE; - } - - GST_PLUGIN_FEATURE (factory)->plugin_name = plugin->desc.name; - GST_PLUGIN_FEATURE (factory)->loaded = TRUE; - - gst_registry_add_feature (gst_registry_get_default (), - GST_PLUGIN_FEATURE (factory)); - - return TRUE; -} -#endif diff --git a/libs/gst/base/gstpushsrc.c b/libs/gst/base/gstpushsrc.c deleted file mode 100644 index db0a6d3328..0000000000 --- a/libs/gst/base/gstpushsrc.c +++ /dev/null @@ -1,173 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> - * 2000,2005 Wim Taymans <wim@fluendo.com> - * - * gstpushsrc.c: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * SECTION:gstpushsrc - * @title: GstPushSrc - * @short_description: Base class for push based source elements - * @see_also: #GstBaseSrc - * - * This class is mostly useful for elements that cannot do - * random access, or at least very slowly. The source usually - * prefers to push out a fixed size buffer. - * - * Subclasses usually operate in a format that is different from the - * default GST_FORMAT_BYTES format of #GstBaseSrc. - * - * Classes extending this base class will usually be scheduled - * in a push based mode. If the peer accepts to operate without - * offsets and within the limits of the allowed block size, this - * class can operate in getrange based mode automatically. To make - * this possible, the subclass should implement and override the - * SCHEDULING query. - * - * The subclass should extend the methods from the baseclass in - * addition to the ::create method. - * - * Seeking, flushing, scheduling and sync is all handled by this - * base class. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include <stdlib.h> -#include <string.h> - -#include "gstpushsrc.h" -#include "gsttypefindhelper.h" - -GST_DEBUG_CATEGORY_STATIC (gst_push_src_debug); -#define GST_CAT_DEFAULT gst_push_src_debug - -#define _do_init \ - GST_DEBUG_CATEGORY_INIT (gst_push_src_debug, "pushsrc", 0, \ - "pushsrc element"); - -#define gst_push_src_parent_class parent_class -G_DEFINE_TYPE_WITH_CODE (GstPushSrc, gst_push_src, GST_TYPE_BASE_SRC, _do_init); - -static gboolean gst_push_src_query (GstBaseSrc * src, GstQuery * query); -static GstFlowReturn gst_push_src_create (GstBaseSrc * bsrc, guint64 offset, - guint length, GstBuffer ** ret); -static GstFlowReturn gst_push_src_alloc (GstBaseSrc * bsrc, guint64 offset, - guint length, GstBuffer ** ret); -static GstFlowReturn gst_push_src_fill (GstBaseSrc * bsrc, guint64 offset, - guint length, GstBuffer * ret); - -static void -gst_push_src_class_init (GstPushSrcClass * klass) -{ - GstBaseSrcClass *gstbasesrc_class = (GstBaseSrcClass *) klass; - - gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_push_src_create); - gstbasesrc_class->alloc = GST_DEBUG_FUNCPTR (gst_push_src_alloc); - gstbasesrc_class->fill = GST_DEBUG_FUNCPTR (gst_push_src_fill); - gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_push_src_query); -} - -static void -gst_push_src_init (GstPushSrc * pushsrc) -{ - /* nop */ -} - -static gboolean -gst_push_src_query (GstBaseSrc * src, GstQuery * query) -{ - gboolean ret; - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_SCHEDULING: - { - /* a pushsrc can by default never operate in pull mode override - * if you want something different. */ - gst_query_set_scheduling (query, GST_SCHEDULING_FLAG_SEQUENTIAL, 1, -1, - 0); - gst_query_add_scheduling_mode (query, GST_PAD_MODE_PUSH); - - ret = TRUE; - break; - } - default: - ret = GST_BASE_SRC_CLASS (parent_class)->query (src, query); - break; - } - return ret; -} - - -static GstFlowReturn -gst_push_src_create (GstBaseSrc * bsrc, guint64 offset, guint length, - GstBuffer ** ret) -{ - GstFlowReturn fret; - GstPushSrc *src; - GstPushSrcClass *pclass; - - src = GST_PUSH_SRC (bsrc); - pclass = GST_PUSH_SRC_GET_CLASS (src); - if (pclass->create) - fret = pclass->create (src, ret); - else - fret = - GST_BASE_SRC_CLASS (parent_class)->create (bsrc, offset, length, ret); - - return fret; -} - -static GstFlowReturn -gst_push_src_alloc (GstBaseSrc * bsrc, guint64 offset, guint length, - GstBuffer ** ret) -{ - GstFlowReturn fret; - GstPushSrc *src; - GstPushSrcClass *pclass; - - src = GST_PUSH_SRC (bsrc); - pclass = GST_PUSH_SRC_GET_CLASS (src); - if (pclass->alloc) - fret = pclass->alloc (src, ret); - else - fret = GST_BASE_SRC_CLASS (parent_class)->alloc (bsrc, offset, length, ret); - - return fret; -} - -static GstFlowReturn -gst_push_src_fill (GstBaseSrc * bsrc, guint64 offset, guint length, - GstBuffer * ret) -{ - GstFlowReturn fret; - GstPushSrc *src; - GstPushSrcClass *pclass; - - src = GST_PUSH_SRC (bsrc); - pclass = GST_PUSH_SRC_GET_CLASS (src); - if (pclass->fill) - fret = pclass->fill (src, ret); - else - fret = GST_BASE_SRC_CLASS (parent_class)->fill (bsrc, offset, length, ret); - - return fret; -} diff --git a/libs/gst/base/gstpushsrc.h b/libs/gst/base/gstpushsrc.h deleted file mode 100644 index 7dd53b6f07..0000000000 --- a/libs/gst/base/gstpushsrc.h +++ /dev/null @@ -1,92 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> - * 2000 Wim Taymans <wtay@chello.be> - * 2005 Wim Taymans <wim@fluendo.com> - * - * gstpushsrc.h: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_PUSH_SRC_H__ -#define __GST_PUSH_SRC_H__ - -#include <gst/gst.h> -#include <gst/base/gstbasesrc.h> - -G_BEGIN_DECLS - -#define GST_TYPE_PUSH_SRC (gst_push_src_get_type()) -#define GST_PUSH_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PUSH_SRC,GstPushSrc)) -#define GST_PUSH_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PUSH_SRC,GstPushSrcClass)) -#define GST_PUSH_SRC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PUSH_SRC, GstPushSrcClass)) -#define GST_IS_PUSH_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PUSH_SRC)) -#define GST_IS_PUSH_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PUSH_SRC)) - -typedef struct _GstPushSrc GstPushSrc; -typedef struct _GstPushSrcClass GstPushSrcClass; - -/** - * GstPushSrc: - * - * The opaque #GstPushSrc data structure. - */ -struct _GstPushSrc { - GstBaseSrc parent; - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; -}; - -/** - * GstPushSrcClass: - * @parent_class: Element parent class - * @create: Ask the subclass to create a buffer. The subclass decides which - * size this buffer should be. Other then that, refer to - * #GstBaseSrc<!-- -->.create() for more details. If this method is - * not implemented, @alloc followed by @fill will be called. - * @alloc: Ask the subclass to allocate a buffer. The subclass decides which - * size this buffer should be. The default implementation will create - * a new buffer from the negotiated allocator. - * @fill: Ask the subclass to fill the buffer with data. - * - * Subclasses can override any of the available virtual methods or not, as - * needed. At the minimum, the @fill method should be overridden to produce - * buffers. - */ -struct _GstPushSrcClass { - GstBaseSrcClass parent_class; - - /* ask the subclass to create a buffer, the default implementation - * uses alloc and fill */ - GstFlowReturn (*create) (GstPushSrc *src, GstBuffer **buf); - /* allocate memory for a buffer */ - GstFlowReturn (*alloc) (GstPushSrc *src, GstBuffer **buf); - /* ask the subclass to fill a buffer */ - GstFlowReturn (*fill) (GstPushSrc *src, GstBuffer *buf); - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; -}; - -GST_BASE_API -GType gst_push_src_get_type (void); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstPushSrc, gst_object_unref) - -G_END_DECLS - -#endif /* __GST_PUSH_SRC_H__ */ diff --git a/libs/gst/base/gstqueuearray.c b/libs/gst/base/gstqueuearray.c deleted file mode 100644 index 1cf1747518..0000000000 --- a/libs/gst/base/gstqueuearray.c +++ /dev/null @@ -1,772 +0,0 @@ -/* GStreamer - * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com> - * Copyright (C) 2015 Tim-Philipp Müller <tim@centricular.com> - * - * gstqueuearray.c: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * SECTION:gstqueuearray - * @title: GstQueueArray - * @short_description: Array based queue object - * - * #GstQueueArray is an object that provides standard queue functionality - * based on an array instead of linked lists. This reduces the overhead - * caused by memory management by a large factor. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <string.h> -#include <gst/gst.h> -#include "gstqueuearray.h" - -struct _GstQueueArray -{ - /* < private > */ - guint8 *array; - guint size; - guint head; - guint tail; - guint length; - guint elt_size; - gboolean struct_array; - GDestroyNotify clear_func; -}; - -/** - * gst_queue_array_new_for_struct: (skip) - * @struct_size: Size of each element (e.g. structure) in the array - * @initial_size: Initial size of the new queue - * - * Allocates a new #GstQueueArray object for elements (e.g. structures) - * of size @struct_size, with an initial queue size of @initial_size. - * - * Returns: a new #GstQueueArray object - * - * Since: 1.6 - */ -GstQueueArray * -gst_queue_array_new_for_struct (gsize struct_size, guint initial_size) -{ - GstQueueArray *array; - - g_return_val_if_fail (struct_size > 0, NULL); - - array = g_slice_new (GstQueueArray); - array->elt_size = struct_size; - array->size = initial_size; - array->array = g_malloc0 (struct_size * initial_size); - array->head = 0; - array->tail = 0; - array->length = 0; - array->struct_array = TRUE; - array->clear_func = NULL; - return array; -} - -/** - * gst_queue_array_new: (skip) - * @initial_size: Initial size of the new queue - * - * Allocates a new #GstQueueArray object with an initial - * queue size of @initial_size. - * - * Returns: a new #GstQueueArray object - * - * Since: 1.2 - */ -GstQueueArray * -gst_queue_array_new (guint initial_size) -{ - GstQueueArray *array; - - array = gst_queue_array_new_for_struct (sizeof (gpointer), initial_size); - array->struct_array = FALSE; - return array; -} - -/** - * gst_queue_array_free: (skip) - * @array: a #GstQueueArray object - * - * Frees queue @array and all memory associated to it. - * - * Since: 1.2 - */ -void -gst_queue_array_free (GstQueueArray * array) -{ - g_return_if_fail (array != NULL); - gst_queue_array_clear (array); - g_free (array->array); - g_slice_free (GstQueueArray, array); -} - -/** - * gst_queue_array_set_clear_func: (skip) - * @array: a #GstQueueArray object - * @clear_func: a function to clear an element of @array - * - * Sets a function to clear an element of @array. - * - * The @clear_func will be called when an element in the array - * data segment is removed and when the array is freed and data - * segment is deallocated as well. @clear_func will be passed a - * pointer to the element to clear, rather than the element itself. - * - * Note that in contrast with other uses of #GDestroyNotify - * functions, @clear_func is expected to clear the contents of - * the array element it is given, but not free the element itself. - * - * Since: 1.16 - */ -void -gst_queue_array_set_clear_func (GstQueueArray * array, - GDestroyNotify clear_func) -{ - g_return_if_fail (array != NULL); - array->clear_func = clear_func; -} - -static void -gst_queue_array_clear_idx (GstQueueArray * array, guint idx) -{ - guint pos; - - if (!array->clear_func) - return; - - pos = (idx + array->head) % array->size; - if (array->struct_array) - array->clear_func (array->array + pos * array->elt_size); - else - array->clear_func (*(gpointer *) (array->array + pos * array->elt_size)); -} - -/** - * gst_queue_array_clear: (skip) - * @array: a #GstQueueArray object - * - * Clears queue @array and frees all memory associated to it. - * - * Since: 1.16 - */ -void -gst_queue_array_clear (GstQueueArray * array) -{ - g_return_if_fail (array != NULL); - - if (array->clear_func != NULL) { - guint i; - - for (i = 0; i < array->length; i++) { - gst_queue_array_clear_idx (array, i); - } - } - - array->head = 0; - array->tail = 0; - array->length = 0; -} - -/** - * gst_queue_array_pop_head_struct: (skip) - * @array: a #GstQueueArray object - * - * Returns the head of the queue @array and removes it from the queue. - * - * Returns: pointer to element or struct, or NULL if @array was empty. The - * data pointed to by the returned pointer stays valid only as long as - * the queue array is not modified further! - * - * Since: 1.6 - */ -gpointer -gst_queue_array_pop_head_struct (GstQueueArray * array) -{ - gpointer p_struct; - g_return_val_if_fail (array != NULL, NULL); - /* empty array */ - if (G_UNLIKELY (array->length == 0)) - return NULL; - - p_struct = array->array + (array->elt_size * array->head); - - array->head++; - array->head %= array->size; - array->length--; - - return p_struct; -} - -/** - * gst_queue_array_pop_head: (skip) - * @array: a #GstQueueArray object - * - * Returns and head of the queue @array and removes - * it from the queue. - * - * Returns: The head of the queue - * - * Since: 1.2 - */ -gpointer -gst_queue_array_pop_head (GstQueueArray * array) -{ - gpointer ret; - g_return_val_if_fail (array != NULL, NULL); - - /* empty array */ - if (G_UNLIKELY (array->length == 0)) - return NULL; - - ret = *(gpointer *) (array->array + (sizeof (gpointer) * array->head)); - array->head++; - array->head %= array->size; - array->length--; - return ret; -} - -/** - * gst_queue_array_peek_head_struct: (skip) - * @array: a #GstQueueArray object - * - * Returns the head of the queue @array without removing it from the queue. - * - * Returns: pointer to element or struct, or NULL if @array was empty. The - * data pointed to by the returned pointer stays valid only as long as - * the queue array is not modified further! - * - * Since: 1.6 - */ -gpointer -gst_queue_array_peek_head_struct (GstQueueArray * array) -{ - g_return_val_if_fail (array != NULL, NULL); - /* empty array */ - if (G_UNLIKELY (array->length == 0)) - return NULL; - - return array->array + (array->elt_size * array->head); -} - -/** - * gst_queue_array_peek_head: (skip) - * @array: a #GstQueueArray object - * - * Returns the head of the queue @array and does not - * remove it from the queue. - * - * Returns: The head of the queue - * - * Since: 1.2 - */ -gpointer -gst_queue_array_peek_head (GstQueueArray * array) -{ - g_return_val_if_fail (array != NULL, NULL); - /* empty array */ - if (G_UNLIKELY (array->length == 0)) - return NULL; - - return *(gpointer *) (array->array + (sizeof (gpointer) * array->head)); -} - -/** - * gst_queue_array_peek_nth: (skip) - * - * Returns the item at @idx in @array, but does not remove it from the queue. - * - * Returns: The item, or %NULL if @idx was out of bounds - * - * Since: 1.16 - */ -gpointer -gst_queue_array_peek_nth (GstQueueArray * array, guint idx) -{ - g_return_val_if_fail (array != NULL, NULL); - g_return_val_if_fail (idx < array->length, NULL); - - idx = (array->head + idx) % array->size; - - return *(gpointer *) (array->array + (sizeof (gpointer) * idx)); -} - -/** - * gst_queue_array_peek_nth_struct: (skip) - * - * Returns the item at @idx in @array, but does not remove it from the queue. - * - * Returns: The item, or %NULL if @idx was out of bounds - * - * Since: 1.16 - */ -gpointer -gst_queue_array_peek_nth_struct (GstQueueArray * array, guint idx) -{ - g_return_val_if_fail (array != NULL, NULL); - g_return_val_if_fail (idx < array->length, NULL); - - idx = (array->head + idx) % array->size; - - return array->array + (array->elt_size * idx); -} - -static void -gst_queue_array_do_expand (GstQueueArray * array) -{ - guint elt_size = array->elt_size; - /* newsize is 50% bigger */ - guint oldsize = array->size; - guint newsize = MAX ((3 * oldsize) / 2, oldsize + 1); - - /* copy over data */ - if (array->tail != 0) { - guint8 *array2 = g_malloc0 (elt_size * newsize); - guint t1 = array->head; - guint t2 = oldsize - array->head; - - /* [0-----TAIL][HEAD------SIZE] - * - * We want to end up with - * [HEAD------------------TAIL][----FREEDATA------NEWSIZE] - * - * 1) move [HEAD-----SIZE] part to beginning of new array - * 2) move [0-------TAIL] part new array, after previous part - */ - - memcpy (array2, array->array + (elt_size * array->head), t2 * elt_size); - memcpy (array2 + t2 * elt_size, array->array, t1 * elt_size); - - g_free (array->array); - array->array = array2; - array->head = 0; - } else { - /* Fast path, we just need to grow the array */ - array->array = g_realloc (array->array, elt_size * newsize); - memset (array->array + elt_size * oldsize, 0, - elt_size * (newsize - oldsize)); - } - array->tail = oldsize; - array->size = newsize; -} - -/** - * gst_queue_array_push_element_tail: (skip) - * @array: a #GstQueueArray object - * @p_struct: address of element or structure to push to the tail of the queue - * - * Pushes the element at address @p_struct to the tail of the queue @array - * (Copies the contents of a structure of the struct_size specified when - * creating the queue into the array). - * - * Since: 1.6 - */ -void -gst_queue_array_push_tail_struct (GstQueueArray * array, gpointer p_struct) -{ - guint elt_size; - - g_return_if_fail (p_struct != NULL); - g_return_if_fail (array != NULL); - elt_size = array->elt_size; - - /* Check if we need to make room */ - if (G_UNLIKELY (array->length == array->size)) - gst_queue_array_do_expand (array); - - memcpy (array->array + elt_size * array->tail, p_struct, elt_size); - array->tail++; - array->tail %= array->size; - array->length++; -} - -/** - * gst_queue_array_push_tail: (skip) - * @array: a #GstQueueArray object - * @data: object to push - * - * Pushes @data to the tail of the queue @array. - * - * Since: 1.2 - */ -void -gst_queue_array_push_tail (GstQueueArray * array, gpointer data) -{ - g_return_if_fail (array != NULL); - - /* Check if we need to make room */ - if (G_UNLIKELY (array->length == array->size)) - gst_queue_array_do_expand (array); - - *(gpointer *) (array->array + sizeof (gpointer) * array->tail) = data; - array->tail++; - array->tail %= array->size; - array->length++; -} - -/** - * gst_queue_array_peek_tail: (skip) - * @array: a #GstQueueArray object - * - * Returns the tail of the queue @array, but does not remove it from the queue. - * - * Returns: The tail of the queue - * - * Since: 1.14 - */ -gpointer -gst_queue_array_peek_tail (GstQueueArray * array) -{ - guint len, idx; - - g_return_val_if_fail (array != NULL, NULL); - - len = array->length; - - /* empty array */ - if (len == 0) - return NULL; - - idx = (array->head + (len - 1)) % array->size; - - return *(gpointer *) (array->array + (sizeof (gpointer) * idx)); -} - -/** - * gst_queue_array_peek_tail_struct: (skip) - * @array: a #GstQueueArray object - * - * Returns the tail of the queue @array, but does not remove it from the queue. - * - * Returns: The tail of the queue - * - * Since: 1.14 - */ -gpointer -gst_queue_array_peek_tail_struct (GstQueueArray * array) -{ - guint len, idx; - - g_return_val_if_fail (array != NULL, NULL); - - len = array->length; - - /* empty array */ - if (len == 0) - return NULL; - - idx = (array->head + (len - 1)) % array->size; - - return array->array + (array->elt_size * idx); -} - -/** - * gst_queue_array_pop_tail: (skip) - * @array: a #GstQueueArray object - * - * Returns the tail of the queue @array and removes - * it from the queue. - * - * Returns: The tail of the queue - * - * Since: 1.14 - */ -gpointer -gst_queue_array_pop_tail (GstQueueArray * array) -{ - gpointer ret; - guint len, idx; - - g_return_val_if_fail (array != NULL, NULL); - - len = array->length; - - /* empty array */ - if (len == 0) - return NULL; - - idx = (array->head + (len - 1)) % array->size; - - ret = *(gpointer *) (array->array + (sizeof (gpointer) * idx)); - - array->tail = idx; - array->length--; - - return ret; -} - -/** - * gst_queue_array_pop_tail_struct: (skip) - * @array: a #GstQueueArray object - * - * Returns the tail of the queue @array and removes - * it from the queue. - * - * Returns: The tail of the queue - * - * Since: 1.14 - */ -gpointer -gst_queue_array_pop_tail_struct (GstQueueArray * array) -{ - gpointer ret; - guint len, idx; - - g_return_val_if_fail (array != NULL, NULL); - - len = array->length; - - /* empty array */ - if (len == 0) - return NULL; - - idx = (array->head + (len - 1)) % array->size; - - ret = array->array + (array->elt_size * idx); - - array->tail = idx; - array->length--; - - return ret; -} - -/** - * gst_queue_array_is_empty: (skip) - * @array: a #GstQueueArray object - * - * Checks if the queue @array is empty. - * - * Returns: %TRUE if the queue @array is empty - * - * Since: 1.2 - */ -gboolean -gst_queue_array_is_empty (GstQueueArray * array) -{ - g_return_val_if_fail (array != NULL, FALSE); - return (array->length == 0); -} - - -/** - * gst_queue_array_drop_struct: (skip) - * @array: a #GstQueueArray object - * @idx: index to drop - * @p_struct: address into which to store the data of the dropped structure, or NULL - * - * Drops the queue element at position @idx from queue @array and copies the - * data of the element or structure that was removed into @p_struct if - * @p_struct is set (not NULL). - * - * Returns: TRUE on success, or FALSE on error - * - * Since: 1.6 - */ -gboolean -gst_queue_array_drop_struct (GstQueueArray * array, guint idx, - gpointer p_struct) -{ - int first_item_index, last_item_index; - guint actual_idx; - guint elt_size; - - g_return_val_if_fail (array != NULL, FALSE); - actual_idx = (array->head + idx) % array->size; - - g_return_val_if_fail (array->length > 0, FALSE); - g_return_val_if_fail (actual_idx < array->size, FALSE); - - elt_size = array->elt_size; - - first_item_index = array->head; - - /* tail points to the first free spot */ - last_item_index = (array->tail - 1 + array->size) % array->size; - - if (p_struct != NULL) - memcpy (p_struct, array->array + elt_size * actual_idx, elt_size); - - /* simple case actual_idx == first item */ - if (actual_idx == first_item_index) { - /* clear current head position if needed */ - if (p_struct == NULL) - gst_queue_array_clear_idx (array, idx); - - /* move the head plus one */ - array->head++; - array->head %= array->size; - array->length--; - return TRUE; - } - - /* simple case idx == last item */ - if (actual_idx == last_item_index) { - /* clear current tail position if needed */ - if (p_struct == NULL) - gst_queue_array_clear_idx (array, idx); - - /* move tail minus one, potentially wrapping */ - array->tail = (array->tail - 1 + array->size) % array->size; - array->length--; - return TRUE; - } - - /* non-wrapped case */ - if (first_item_index < last_item_index) { - /* clear idx if needed */ - if (p_struct == NULL) - gst_queue_array_clear_idx (array, idx); - - g_assert (first_item_index < actual_idx && actual_idx < last_item_index); - /* move everything beyond actual_idx one step towards zero in array */ - memmove (array->array + elt_size * actual_idx, - array->array + elt_size * (actual_idx + 1), - (last_item_index - actual_idx) * elt_size); - /* tail might wrap, ie if tail == 0 (and last_item_index == size) */ - array->tail = (array->tail - 1 + array->size) % array->size; - array->length--; - return TRUE; - } - - /* only wrapped cases left */ - g_assert (first_item_index > last_item_index); - - if (actual_idx < last_item_index) { - /* clear idx if needed */ - if (p_struct == NULL) - gst_queue_array_clear_idx (array, idx); - - /* actual_idx is before last_item_index, move data towards zero */ - memmove (array->array + elt_size * actual_idx, - array->array + elt_size * (actual_idx + 1), - (last_item_index - actual_idx) * elt_size); - /* tail should not wrap in this case! */ - g_assert (array->tail > 0); - array->tail--; - array->length--; - return TRUE; - } - - if (actual_idx > first_item_index) { - /* clear idx if needed */ - if (p_struct == NULL) - gst_queue_array_clear_idx (array, idx); - - /* actual_idx is after first_item_index, move data to higher indices */ - memmove (array->array + elt_size * (first_item_index + 1), - array->array + elt_size * first_item_index, - (actual_idx - first_item_index) * elt_size); - array->head++; - /* head should not wrap in this case! */ - g_assert (array->head < array->size); - array->length--; - return TRUE; - } - - g_return_val_if_reached (FALSE); -} - -/** - * gst_queue_array_drop_element: (skip) - * @array: a #GstQueueArray object - * @idx: index to drop - * - * Drops the queue element at position @idx from queue @array. - * - * Returns: the dropped element - * - * Since: 1.2 - */ -gpointer -gst_queue_array_drop_element (GstQueueArray * array, guint idx) -{ - gpointer ptr; - - if (!gst_queue_array_drop_struct (array, idx, &ptr)) - return NULL; - - return ptr; -} - -/** - * gst_queue_array_find: (skip) - * @array: a #GstQueueArray object - * @func: (allow-none): comparison function, or %NULL to find @data by value - * @data: data for comparison function - * - * Finds an element in the queue @array, either by comparing every element - * with @func or by looking up @data if no compare function @func is provided, - * and returning the index of the found element. - * - * Returns: Index of the found element or -1 if nothing was found. - * - * Since: 1.2 - */ -guint -gst_queue_array_find (GstQueueArray * array, GCompareFunc func, gpointer data) -{ - gpointer p_element; - guint elt_size; - guint i; - - /* For struct arrays we need to implement this differently so that - * the user gets a pointer to the element data not the dereferenced - * pointer itself */ - - g_return_val_if_fail (array != NULL, -1); - g_return_val_if_fail (array->struct_array == FALSE, -1); - - elt_size = array->elt_size; - - if (func != NULL) { - /* Scan from head to tail */ - for (i = 0; i < array->length; i++) { - p_element = array->array + ((i + array->head) % array->size) * elt_size; - if (func (*(gpointer *) p_element, data) == 0) - return i; - } - } else { - for (i = 0; i < array->length; i++) { - p_element = array->array + ((i + array->head) % array->size) * elt_size; - if (*(gpointer *) p_element == data) - return i; - } - } - - return -1; -} - -/** - * gst_queue_array_get_length: (skip) - * @array: a #GstQueueArray object - * - * Returns the length of the queue @array - * - * Returns: the length of the queue @array. - * - * Since: 1.2 - */ -guint -gst_queue_array_get_length (GstQueueArray * array) -{ - g_return_val_if_fail (array != NULL, 0); - return array->length; -} diff --git a/libs/gst/base/gstqueuearray.h b/libs/gst/base/gstqueuearray.h deleted file mode 100644 index 77edec0990..0000000000 --- a/libs/gst/base/gstqueuearray.h +++ /dev/null @@ -1,109 +0,0 @@ -/* GStreamer - * Copyright (C) 2009-2010 Edward Hervey <bilboed@bilboed.com> - * - * gstqueuearray.h: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include <glib.h> - -#ifndef __GST_QUEUE_ARRAY_H__ -#define __GST_QUEUE_ARRAY_H__ - -#include <gst/base/base-prelude.h> - -G_BEGIN_DECLS - -/** - * GstQueueArray: (skip) - */ -typedef struct _GstQueueArray GstQueueArray; - -GST_BASE_API -GstQueueArray * gst_queue_array_new (guint initial_size); - -GST_BASE_API -void gst_queue_array_free (GstQueueArray * array); - -GST_BASE_API -void gst_queue_array_set_clear_func (GstQueueArray *array, - GDestroyNotify clear_func); - -GST_BASE_API -void gst_queue_array_clear (GstQueueArray * array); - -GST_BASE_API -gpointer gst_queue_array_pop_head (GstQueueArray * array); - -GST_BASE_API -gpointer gst_queue_array_peek_head (GstQueueArray * array); - -GST_BASE_API -gpointer gst_queue_array_peek_nth (GstQueueArray * array, guint idx); - -GST_BASE_API -gpointer gst_queue_array_pop_tail (GstQueueArray * array); - -GST_BASE_API -gpointer gst_queue_array_peek_tail (GstQueueArray * array); - -GST_BASE_API -void gst_queue_array_push_tail (GstQueueArray * array, - gpointer data); -GST_BASE_API -gboolean gst_queue_array_is_empty (GstQueueArray * array); - -GST_BASE_API -gpointer gst_queue_array_drop_element (GstQueueArray * array, - guint idx); -GST_BASE_API -guint gst_queue_array_find (GstQueueArray * array, - GCompareFunc func, - gpointer data); -GST_BASE_API -guint gst_queue_array_get_length (GstQueueArray * array); - -/* Functions for use with structures */ - -GST_BASE_API -GstQueueArray * gst_queue_array_new_for_struct (gsize struct_size, - guint initial_size); -GST_BASE_API -void gst_queue_array_push_tail_struct (GstQueueArray * array, - gpointer p_struct); -GST_BASE_API -gpointer gst_queue_array_pop_head_struct (GstQueueArray * array); - -GST_BASE_API -gpointer gst_queue_array_peek_head_struct (GstQueueArray * array); - -GST_BASE_API -gpointer gst_queue_array_peek_nth_struct (GstQueueArray * array, guint idx); - -GST_BASE_API -gboolean gst_queue_array_drop_struct (GstQueueArray * array, - guint idx, - gpointer p_struct); -GST_BASE_API -gpointer gst_queue_array_pop_tail_struct (GstQueueArray * array); - -GST_BASE_API -gpointer gst_queue_array_peek_tail_struct (GstQueueArray * array); - -G_END_DECLS - -#endif diff --git a/libs/gst/base/gsttypefindhelper.c b/libs/gst/base/gsttypefindhelper.c deleted file mode 100644 index 56003d500a..0000000000 --- a/libs/gst/base/gsttypefindhelper.c +++ /dev/null @@ -1,832 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> - * Copyright (C) 2000,2005 Wim Taymans <wim@fluendo.com> - * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> - * - * gsttypefindhelper.c: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * SECTION:gsttypefindhelper - * @title: GstTypeFindHelper - * @short_description: Utility functions for typefinding - * - * Utility functions for elements doing typefinding: - * gst_type_find_helper() does typefinding in pull mode, while - * gst_type_find_helper_for_buffer() is useful for elements needing to do - * typefinding in push mode from a chain function. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include <stdlib.h> -#include <string.h> - -#include "gsttypefindhelper.h" - -/* ********************** typefinding in pull mode ************************ */ - -static void -helper_find_suggest (gpointer data, guint probability, GstCaps * caps); - -typedef struct -{ - GstBuffer *buffer; - GstMapInfo map; -} GstMappedBuffer; - -typedef struct -{ - GSList *buffers; /* buffer cache */ - guint64 size; - guint64 last_offset; - GstTypeFindHelperGetRangeFunction func; - GstTypeFindProbability best_probability; - GstCaps *caps; - GstTypeFindFactory *factory; /* for logging */ - GstObject *obj; /* for logging */ - GstObject *parent; - GstFlowReturn flow_ret; -} GstTypeFindHelper; - -/* - * helper_find_peek: - * @data: helper data struct - * @off: stream offset - * @size: block size - * - * Get data pointer within a stream. Keeps a cache of read buffers (partly - * for performance reasons, but mostly because pointers returned by us need - * to stay valid until typefinding has finished) - * - * Returns: (nullable): address of the data or %NULL if buffer does not cover - * the requested range. - */ -static const guint8 * -helper_find_peek (gpointer data, gint64 offset, guint size) -{ - GstTypeFindHelper *helper; - GstBuffer *buffer; - GSList *insert_pos = NULL; - gsize buf_size; - guint64 buf_offset; - GstMappedBuffer *bmap; -#if 0 - GstCaps *caps; -#endif - - helper = (GstTypeFindHelper *) data; - - GST_LOG_OBJECT (helper->obj, "'%s' called peek (%" G_GINT64_FORMAT - ", %u)", GST_OBJECT_NAME (helper->factory), offset, size); - - if (size == 0) - return NULL; - - if (offset < 0) { - if (helper->size == -1 || helper->size < -offset) - return NULL; - - offset += helper->size; - } - - /* see if we have a matching buffer already in our list */ - if (size > 0 && offset <= helper->last_offset) { - GSList *walk; - - for (walk = helper->buffers; walk; walk = walk->next) { - GstMappedBuffer *bmp = (GstMappedBuffer *) walk->data; - GstBuffer *buf = GST_BUFFER_CAST (bmp->buffer); - - buf_offset = GST_BUFFER_OFFSET (buf); - buf_size = bmp->map.size; - - /* buffers are kept sorted by end offset (highest first) in the list, so - * at this point we save the current position and stop searching if - * we're after the searched end offset */ - if (buf_offset <= offset) { - if ((offset + size) < (buf_offset + buf_size)) { - /* must already have been mapped before */ - return (guint8 *) bmp->map.data + (offset - buf_offset); - } - } else if (offset + size >= buf_offset + buf_size) { - insert_pos = walk; - break; - } - } - } - - buffer = NULL; - /* some typefinders go in 1 byte steps over 1k of data and request - * small buffers. It is really inefficient to pull each time, and pulling - * a larger chunk is almost free. Trying to pull a larger chunk at the end - * of the file is also not a problem here, we'll just get a truncated buffer - * in that case (and we'll have to double-check the size we actually get - * anyway, see below) */ - helper->flow_ret = - helper->func (helper->obj, helper->parent, offset, MAX (size, 4096), - &buffer); - - if (helper->flow_ret != GST_FLOW_OK) - goto error; - -#if 0 - caps = GST_BUFFER_CAPS (buffer); - - if (caps && !gst_caps_is_empty (caps) && !gst_caps_is_any (caps)) { - GST_DEBUG ("buffer has caps %" GST_PTR_FORMAT ", suggest max probability", - caps); - - gst_caps_replace (&helper->caps, caps); - helper->best_probability = GST_TYPE_FIND_MAXIMUM; - - gst_buffer_unref (buffer); - return NULL; - } -#endif - - /* getrange might silently return shortened buffers at the end of a file, - * we must, however, always return either the full requested data or %NULL */ - buf_offset = GST_BUFFER_OFFSET (buffer); - buf_size = gst_buffer_get_size (buffer); - - if (buf_size < size) { - GST_DEBUG ("dropping short buffer of size %" G_GSIZE_FORMAT "," - "requested size was %u", buf_size, size); - gst_buffer_unref (buffer); - return NULL; - } - - if (buf_offset != -1 && buf_offset != offset) { - GST_DEBUG ("dropping buffer with unexpected offset %" G_GUINT64_FORMAT ", " - "expected offset was %" G_GUINT64_FORMAT, buf_offset, offset); - gst_buffer_unref (buffer); - return NULL; - } - - bmap = g_slice_new0 (GstMappedBuffer); - - if (!gst_buffer_map (buffer, &bmap->map, GST_MAP_READ)) - goto map_failed; - - bmap->buffer = buffer; - - if (insert_pos) { - helper->buffers = g_slist_insert_before (helper->buffers, insert_pos, bmap); - } else { - /* if insert_pos is not set, our offset is bigger than the largest offset - * we have so far; since we keep the list sorted with highest offsets - * first, we need to prepend the buffer to the list */ - helper->last_offset = GST_BUFFER_OFFSET (buffer) + buf_size; - helper->buffers = g_slist_prepend (helper->buffers, bmap); - } - - return bmap->map.data; - -error: - { - GST_INFO ("typefind function returned: %s", - gst_flow_get_name (helper->flow_ret)); - return NULL; - } -map_failed: - { - GST_ERROR ("map failed"); - gst_buffer_unref (buffer); - g_slice_free (GstMappedBuffer, bmap); - return NULL; - } -} - -/* - * helper_find_suggest: - * @data: helper data struct - * @probability: probability of the match - * @caps: caps of the type - * - * If given @probability is higher, replace previously store caps. - */ -static void -helper_find_suggest (gpointer data, guint probability, GstCaps * caps) -{ - GstTypeFindHelper *helper = (GstTypeFindHelper *) data; - - GST_LOG_OBJECT (helper->obj, - "'%s' called suggest (%u, %" GST_PTR_FORMAT ")", - GST_OBJECT_NAME (helper->factory), probability, caps); - - if (probability > helper->best_probability) { - gst_caps_replace (&helper->caps, caps); - helper->best_probability = probability; - } -} - -static guint64 -helper_find_get_length (gpointer data) -{ - GstTypeFindHelper *helper = (GstTypeFindHelper *) data; - - GST_LOG_OBJECT (helper->obj, "'%s' called get_length, returning %" - G_GUINT64_FORMAT, GST_OBJECT_NAME (helper->factory), helper->size); - - return helper->size; -} - -static GList * -prioritize_extension (GstObject * obj, GList * type_list, - const gchar * extension) -{ - gint pos = 0; - GList *next, *l; - - if (!extension) - return type_list; - - /* move the typefinders for the extension first in the list. The idea is that - * when one of them returns MAX we don't need to search further as there is a - * very high chance we got the right type. */ - - GST_LOG_OBJECT (obj, "sorting typefind for extension %s to head", extension); - - for (l = type_list; l; l = next) { - const gchar *const *ext; - GstTypeFindFactory *factory; - - next = l->next; - - factory = GST_TYPE_FIND_FACTORY (l->data); - - ext = gst_type_find_factory_get_extensions (factory); - if (ext == NULL) - continue; - - GST_LOG_OBJECT (obj, "testing factory %s for extension %s", - GST_OBJECT_NAME (factory), extension); - - while (*ext != NULL) { - if (strcmp (*ext, extension) == 0) { - /* found extension, move in front */ - GST_LOG_OBJECT (obj, "moving typefind for extension %s to head", - extension); - /* remove entry from list */ - type_list = g_list_delete_link (type_list, l); - /* insert at the position */ - type_list = g_list_insert (type_list, factory, pos); - /* next element will be inserted after this one */ - pos++; - break; - } - ++ext; - } - } - - return type_list; -} - -/** - * gst_type_find_helper_get_range: - * @obj: A #GstObject that will be passed as first argument to @func - * @parent: (allow-none): the parent of @obj or %NULL - * @func: (scope call): A generic #GstTypeFindHelperGetRangeFunction that will - * be used to access data at random offsets when doing the typefinding - * @size: The length in bytes - * @extension: (allow-none): extension of the media, or %NULL - * @prob: (out) (allow-none): location to store the probability of the found - * caps, or %NULL - * - * Utility function to do pull-based typefinding. Unlike gst_type_find_helper() - * however, this function will use the specified function @func to obtain the - * data needed by the typefind functions, rather than operating on a given - * source pad. This is useful mostly for elements like tag demuxers which - * strip off data at the beginning and/or end of a file and want to typefind - * the stripped data stream before adding their own source pad (the specified - * callback can then call the upstream peer pad with offsets adjusted for the - * tag size, for example). - * - * When @extension is not %NULL, this function will first try the typefind - * functions for the given extension, which might speed up the typefinding - * in many cases. - * - * Free-function: gst_caps_unref - * - * Returns: (transfer full) (nullable): the #GstCaps corresponding to the data - * stream. Returns %NULL if no #GstCaps matches the data stream. - */ -GstCaps * -gst_type_find_helper_get_range (GstObject * obj, GstObject * parent, - GstTypeFindHelperGetRangeFunction func, guint64 size, - const gchar * extension, GstTypeFindProbability * prob) -{ - GstCaps *caps = NULL; - - gst_type_find_helper_get_range_full (obj, parent, func, size, extension, - &caps, prob); - - return caps; -} - -/** - * gst_type_find_helper_get_range_full: - * @obj: A #GstObject that will be passed as first argument to @func - * @parent: (allow-none): the parent of @obj or %NULL - * @func: (scope call): A generic #GstTypeFindHelperGetRangeFunction that will - * be used to access data at random offsets when doing the typefinding - * @size: The length in bytes - * @extension: (allow-none): extension of the media, or %NULL - * @caps: (out) (transfer full): returned caps - * @prob: (out) (allow-none): location to store the probability of the found - * caps, or %NULL - * - * Utility function to do pull-based typefinding. Unlike gst_type_find_helper() - * however, this function will use the specified function @func to obtain the - * data needed by the typefind functions, rather than operating on a given - * source pad. This is useful mostly for elements like tag demuxers which - * strip off data at the beginning and/or end of a file and want to typefind - * the stripped data stream before adding their own source pad (the specified - * callback can then call the upstream peer pad with offsets adjusted for the - * tag size, for example). - * - * When @extension is not %NULL, this function will first try the typefind - * functions for the given extension, which might speed up the typefinding - * in many cases. - * - * Returns: the last %GstFlowReturn from pulling a buffer or %GST_FLOW_OK if - * typefinding was successful. - * - * Since: 1.14.3 - */ -GstFlowReturn -gst_type_find_helper_get_range_full (GstObject * obj, GstObject * parent, - GstTypeFindHelperGetRangeFunction func, guint64 size, - const gchar * extension, GstCaps ** caps, GstTypeFindProbability * prob) -{ - GstTypeFindHelper helper; - GstTypeFind find; - GSList *walk; - GList *l, *type_list; - GstCaps *result = NULL; - - g_return_val_if_fail (GST_IS_OBJECT (obj), GST_FLOW_ERROR); - g_return_val_if_fail (func != NULL, GST_FLOW_ERROR); - g_return_val_if_fail (caps != NULL, GST_FLOW_ERROR); - - *caps = NULL; - - helper.buffers = NULL; - helper.size = size; - helper.last_offset = 0; - helper.func = func; - helper.best_probability = GST_TYPE_FIND_NONE; - helper.caps = NULL; - helper.obj = obj; - helper.parent = parent; - helper.flow_ret = GST_FLOW_OK; - - find.data = &helper; - find.peek = helper_find_peek; - find.suggest = helper_find_suggest; - - if (size == 0 || size == (guint64) - 1) { - find.get_length = NULL; - } else { - find.get_length = helper_find_get_length; - } - - type_list = gst_type_find_factory_get_list (); - type_list = prioritize_extension (obj, type_list, extension); - - for (l = type_list; l; l = l->next) { - helper.factory = GST_TYPE_FIND_FACTORY (l->data); - gst_type_find_factory_call_function (helper.factory, &find); - if (helper.best_probability >= GST_TYPE_FIND_MAXIMUM) { - /* Any other flow return can be ignored here, we found - * something before any error with highest probability */ - helper.flow_ret = GST_FLOW_OK; - break; - } else if (helper.flow_ret != GST_FLOW_OK - && helper.flow_ret != GST_FLOW_EOS) { - /* We had less than maximum probability and an error, don't return - * any caps as they might be with a lower probability than what - * we would've gotten when continuing if there was no error */ - gst_caps_replace (&helper.caps, NULL); - break; - } - } - gst_plugin_feature_list_free (type_list); - - for (walk = helper.buffers; walk; walk = walk->next) { - GstMappedBuffer *bmap = (GstMappedBuffer *) walk->data; - - gst_buffer_unmap (bmap->buffer, &bmap->map); - gst_buffer_unref (bmap->buffer); - g_slice_free (GstMappedBuffer, bmap); - } - g_slist_free (helper.buffers); - - if (helper.best_probability > 0) - result = helper.caps; - - if (prob) - *prob = helper.best_probability; - - *caps = result; - if (helper.flow_ret == GST_FLOW_EOS) { - /* Some typefinder might've tried to read too much, if we - * didn't get any meaningful caps because of that this is - * just a normal error */ - helper.flow_ret = GST_FLOW_ERROR; - } - - GST_LOG_OBJECT (obj, "Returning %" GST_PTR_FORMAT " (probability = %u)", - result, (guint) helper.best_probability); - - return helper.flow_ret; -} - -/** - * gst_type_find_helper: - * @src: A source #GstPad - * @size: The length in bytes - * - * Tries to find what type of data is flowing from the given source #GstPad. - * - * Free-function: gst_caps_unref - * - * Returns: (transfer full) (nullable): the #GstCaps corresponding to the data - * stream. Returns %NULL if no #GstCaps matches the data stream. - */ - -GstCaps * -gst_type_find_helper (GstPad * src, guint64 size) -{ - GstTypeFindHelperGetRangeFunction func; - - g_return_val_if_fail (GST_IS_OBJECT (src), NULL); - g_return_val_if_fail (GST_PAD_GETRANGEFUNC (src) != NULL, NULL); - - func = (GstTypeFindHelperGetRangeFunction) (GST_PAD_GETRANGEFUNC (src)); - - return gst_type_find_helper_get_range (GST_OBJECT (src), - GST_OBJECT_PARENT (src), func, size, NULL, NULL); -} - -/* ********************** typefinding for buffers ************************* */ - -typedef struct -{ - const guint8 *data; /* buffer data */ - gsize size; - GstTypeFindProbability best_probability; - GstCaps *caps; - GstTypeFindFactory *factory; /* for logging */ - GstObject *obj; /* for logging */ -} GstTypeFindBufHelper; - -/* - * buf_helper_find_peek: - * @data: helper data struct - * @off: stream offset - * @size: block size - * - * Get data pointer within a buffer. - * - * Returns: (nullable): address inside the buffer or %NULL if buffer does not - * cover the requested range. - */ -static const guint8 * -buf_helper_find_peek (gpointer data, gint64 off, guint size) -{ - GstTypeFindBufHelper *helper; - - helper = (GstTypeFindBufHelper *) data; - GST_LOG_OBJECT (helper->obj, "'%s' called peek (%" G_GINT64_FORMAT ", %u)", - GST_OBJECT_NAME (helper->factory), off, size); - - if (size == 0) - return NULL; - - if (off < 0) { - GST_LOG_OBJECT (helper->obj, "'%s' wanted to peek at end; not supported", - GST_OBJECT_NAME (helper->factory)); - return NULL; - } - - /* If we request beyond the available size, we're sure we can't return - * anything regardless of the requested offset */ - if (size > helper->size) - return NULL; - - /* Only return data if there's enough room left for the given offset. - * This is the same as "if (off + size <= helper->size)" except that - * it doesn't exceed type limits */ - if (off <= helper->size - size) - return helper->data + off; - - return NULL; -} - -/* - * buf_helper_find_suggest: - * @data: helper data struct - * @probability: probability of the match - * @caps: caps of the type - * - * If given @probability is higher, replace previously store caps. - */ -static void -buf_helper_find_suggest (gpointer data, guint probability, GstCaps * caps) -{ - GstTypeFindBufHelper *helper = (GstTypeFindBufHelper *) data; - - GST_LOG_OBJECT (helper->obj, - "'%s' called suggest (%u, %" GST_PTR_FORMAT ")", - GST_OBJECT_NAME (helper->factory), probability, caps); - - /* Note: not >= as we call typefinders in order of rank, highest first */ - if (probability > helper->best_probability) { - gst_caps_replace (&helper->caps, caps); - helper->best_probability = probability; - } -} - -/** - * gst_type_find_helper_for_data: - * @obj: (allow-none): object doing the typefinding, or %NULL (used for logging) - * @data: (transfer none) (array length=size): * a pointer with data to typefind - * @size: the size of @data - * @prob: (out) (allow-none): location to store the probability of the found - * caps, or %NULL - * - * Tries to find what type of data is contained in the given @data, the - * assumption being that the data represents the beginning of the stream or - * file. - * - * All available typefinders will be called on the data in order of rank. If - * a typefinding function returns a probability of %GST_TYPE_FIND_MAXIMUM, - * typefinding is stopped immediately and the found caps will be returned - * right away. Otherwise, all available typefind functions will the tried, - * and the caps with the highest probability will be returned, or %NULL if - * the content of @data could not be identified. - * - * Free-function: gst_caps_unref - * - * Returns: (transfer full) (nullable): the #GstCaps corresponding to the data, - * or %NULL if no type could be found. The caller should free the caps - * returned with gst_caps_unref(). - */ -GstCaps * -gst_type_find_helper_for_data (GstObject * obj, const guint8 * data, gsize size, - GstTypeFindProbability * prob) -{ - return gst_type_find_helper_for_data_with_extension (obj, data, size, NULL, - prob); -} - -/** - * gst_type_find_helper_for_data_with_extension: - * @obj: (allow-none): object doing the typefinding, or %NULL (used for logging) - * @data: (transfer none) (array length=size): * a pointer with data to typefind - * @size: the size of @data - * @extension: (allow-none): extension of the media, or %NULL - * @prob: (out) (allow-none): location to store the probability of the found - * caps, or %NULL - * - * Tries to find what type of data is contained in the given @data, the - * assumption being that the data represents the beginning of the stream or - * file. - * - * All available typefinders will be called on the data in order of rank. If - * a typefinding function returns a probability of %GST_TYPE_FIND_MAXIMUM, - * typefinding is stopped immediately and the found caps will be returned - * right away. Otherwise, all available typefind functions will the tried, - * and the caps with the highest probability will be returned, or %NULL if - * the content of @data could not be identified. - * - * When @extension is not %NULL, this function will first try the typefind - * functions for the given extension, which might speed up the typefinding - * in many cases. - * - * Free-function: gst_caps_unref - * - * Returns: (transfer full) (nullable): the #GstCaps corresponding to the data, - * or %NULL if no type could be found. The caller should free the caps - * returned with gst_caps_unref(). - * - * Since: 1.16 - * - */ -GstCaps * -gst_type_find_helper_for_data_with_extension (GstObject * obj, - const guint8 * data, gsize size, const gchar * extension, - GstTypeFindProbability * prob) -{ - GstTypeFindBufHelper helper; - GstTypeFind find; - GList *l, *type_list; - GstCaps *result = NULL; - - g_return_val_if_fail (data != NULL, NULL); - - helper.data = data; - helper.size = size; - helper.best_probability = GST_TYPE_FIND_NONE; - helper.caps = NULL; - helper.obj = obj; - - if (helper.data == NULL || helper.size == 0) - return NULL; - - find.data = &helper; - find.peek = buf_helper_find_peek; - find.suggest = buf_helper_find_suggest; - find.get_length = NULL; - - type_list = gst_type_find_factory_get_list (); - type_list = prioritize_extension (obj, type_list, extension); - - for (l = type_list; l; l = l->next) { - helper.factory = GST_TYPE_FIND_FACTORY (l->data); - gst_type_find_factory_call_function (helper.factory, &find); - if (helper.best_probability >= GST_TYPE_FIND_MAXIMUM) - break; - } - gst_plugin_feature_list_free (type_list); - - if (helper.best_probability > 0) - result = helper.caps; - - if (prob) - *prob = helper.best_probability; - - GST_LOG_OBJECT (obj, "Returning %" GST_PTR_FORMAT " (probability = %u)", - result, (guint) helper.best_probability); - - return result; -} - -/** - * gst_type_find_helper_for_buffer: - * @obj: (allow-none): object doing the typefinding, or %NULL (used for logging) - * @buf: (in) (transfer none): a #GstBuffer with data to typefind - * @prob: (out) (allow-none): location to store the probability of the found - * caps, or %NULL - * - * Tries to find what type of data is contained in the given #GstBuffer, the - * assumption being that the buffer represents the beginning of the stream or - * file. - * - * All available typefinders will be called on the data in order of rank. If - * a typefinding function returns a probability of %GST_TYPE_FIND_MAXIMUM, - * typefinding is stopped immediately and the found caps will be returned - * right away. Otherwise, all available typefind functions will the tried, - * and the caps with the highest probability will be returned, or %NULL if - * the content of the buffer could not be identified. - * - * Free-function: gst_caps_unref - * - * Returns: (transfer full) (nullable): the #GstCaps corresponding to the data, - * or %NULL if no type could be found. The caller should free the caps - * returned with gst_caps_unref(). - */ -GstCaps * -gst_type_find_helper_for_buffer (GstObject * obj, GstBuffer * buf, - GstTypeFindProbability * prob) -{ - return gst_type_find_helper_for_buffer_with_extension (obj, buf, NULL, prob); -} - -/** - * gst_type_find_helper_for_buffer_with_extension: - * @obj: (allow-none): object doing the typefinding, or %NULL (used for logging) - * @buf: (in) (transfer none): a #GstBuffer with data to typefind - * @extension: (allow-none): extension of the media, or %NULL - * @prob: (out) (allow-none): location to store the probability of the found - * caps, or %NULL - * - * Tries to find what type of data is contained in the given #GstBuffer, the - * assumption being that the buffer represents the beginning of the stream or - * file. - * - * All available typefinders will be called on the data in order of rank. If - * a typefinding function returns a probability of %GST_TYPE_FIND_MAXIMUM, - * typefinding is stopped immediately and the found caps will be returned - * right away. Otherwise, all available typefind functions will the tried, - * and the caps with the highest probability will be returned, or %NULL if - * the content of the buffer could not be identified. - * - * When @extension is not %NULL, this function will first try the typefind - * functions for the given extension, which might speed up the typefinding - * in many cases. - * - * Free-function: gst_caps_unref - * - * Returns: (transfer full) (nullable): the #GstCaps corresponding to the data, - * or %NULL if no type could be found. The caller should free the caps - * returned with gst_caps_unref(). - * - * Since: 1.16 - * - */ -GstCaps * -gst_type_find_helper_for_buffer_with_extension (GstObject * obj, - GstBuffer * buf, const gchar * extension, GstTypeFindProbability * prob) -{ - GstCaps *result; - GstMapInfo info; - - g_return_val_if_fail (buf != NULL, NULL); - g_return_val_if_fail (GST_IS_BUFFER (buf), NULL); - g_return_val_if_fail (GST_BUFFER_OFFSET (buf) == 0 || - GST_BUFFER_OFFSET (buf) == GST_BUFFER_OFFSET_NONE, NULL); - - if (!gst_buffer_map (buf, &info, GST_MAP_READ)) - return NULL; - result = - gst_type_find_helper_for_data_with_extension (obj, info.data, info.size, - extension, prob); - gst_buffer_unmap (buf, &info); - - return result; -} - -/** - * gst_type_find_helper_for_extension: - * @obj: (allow-none): object doing the typefinding, or %NULL (used for logging) - * @extension: an extension - * - * Tries to find the best #GstCaps associated with @extension. - * - * All available typefinders will be checked against the extension in order - * of rank. The caps of the first typefinder that can handle @extension will be - * returned. - * - * Free-function: gst_caps_unref - * - * Returns: (transfer full) (nullable): the #GstCaps corresponding to - * @extension, or %NULL if no type could be found. The caller should free - * the caps returned with gst_caps_unref(). - */ -GstCaps * -gst_type_find_helper_for_extension (GstObject * obj, const gchar * extension) -{ - GList *l, *type_list; - GstCaps *result = NULL; - - g_return_val_if_fail (extension != NULL, NULL); - - GST_LOG_OBJECT (obj, "finding caps for extension %s", extension); - - type_list = gst_type_find_factory_get_list (); - - for (l = type_list; l; l = g_list_next (l)) { - GstTypeFindFactory *factory; - const gchar *const *ext; - - factory = GST_TYPE_FIND_FACTORY (l->data); - - /* we only want to check those factories without a function */ - if (gst_type_find_factory_has_function (factory)) - continue; - - /* get the extension that this typefind factory can handle */ - ext = gst_type_find_factory_get_extensions (factory); - if (ext == NULL) - continue; - - /* there are extension, see if one of them matches the requested - * extension */ - while (*ext != NULL) { - if (strcmp (*ext, extension) == 0) { - /* we found a matching extension, take the caps */ - if ((result = gst_type_find_factory_get_caps (factory))) { - gst_caps_ref (result); - goto done; - } - } - ++ext; - } - } -done: - gst_plugin_feature_list_free (type_list); - - GST_LOG_OBJECT (obj, "Returning %" GST_PTR_FORMAT, result); - - return result; -} diff --git a/libs/gst/base/gsttypefindhelper.h b/libs/gst/base/gsttypefindhelper.h deleted file mode 100644 index bda346cac1..0000000000 --- a/libs/gst/base/gsttypefindhelper.h +++ /dev/null @@ -1,105 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> - * Copyright (C) 2000,2005 Wim Taymans <wim@fluendo.com> - * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net> - * - * gsttypefindhelper.h: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_TYPEFINDHELPER_H__ -#define __GST_TYPEFINDHELPER_H__ - -#include <gst/gst.h> -#include <gst/base/base-prelude.h> - -G_BEGIN_DECLS - -GST_BASE_API -GstCaps * gst_type_find_helper (GstPad *src, guint64 size); - -GST_BASE_API -GstCaps * gst_type_find_helper_for_data (GstObject *obj, - const guint8 *data, - gsize size, - GstTypeFindProbability *prob); - -GST_BASE_API -GstCaps * gst_type_find_helper_for_data_with_extension (GstObject *obj, - const guint8 *data, - gsize size, - const gchar *extension, - GstTypeFindProbability *prob); - -GST_BASE_API -GstCaps * gst_type_find_helper_for_buffer (GstObject *obj, - GstBuffer *buf, - GstTypeFindProbability *prob); - -GST_BASE_API -GstCaps * gst_type_find_helper_for_buffer_with_extension (GstObject *obj, - GstBuffer *buf, - const gchar *extension, - GstTypeFindProbability *prob); - -GST_BASE_API -GstCaps * gst_type_find_helper_for_extension (GstObject * obj, - const gchar * extension); - -/** - * GstTypeFindHelperGetRangeFunction: - * @obj: a #GstObject that will handle the getrange request - * @parent: (allow-none): the parent of @obj or %NULL - * @offset: the offset of the range - * @length: the length of the range - * @buffer: (out): a memory location to hold the result buffer - * - * This function will be called by gst_type_find_helper_get_range() when - * typefinding functions request to peek at the data of a stream at certain - * offsets. If this function returns GST_FLOW_OK, the result buffer will be - * stored in @buffer. The contents of @buffer is invalid for any other - * return value. - * - * This function is supposed to behave exactly like a #GstPadGetRangeFunction. - * - * Returns: GST_FLOW_OK for success - */ -typedef GstFlowReturn (*GstTypeFindHelperGetRangeFunction) (GstObject *obj, - GstObject *parent, - guint64 offset, - guint length, - GstBuffer **buffer); -GST_BASE_API -GstCaps * gst_type_find_helper_get_range (GstObject *obj, - GstObject *parent, - GstTypeFindHelperGetRangeFunction func, - guint64 size, - const gchar *extension, - GstTypeFindProbability *prob); - -GST_BASE_API -GstFlowReturn gst_type_find_helper_get_range_full (GstObject *obj, - GstObject *parent, - GstTypeFindHelperGetRangeFunction func, - guint64 size, - const gchar *extension, - GstCaps **caps, - GstTypeFindProbability *prob); - -G_END_DECLS - -#endif /* __GST_TYPEFINDHELPER_H__ */ diff --git a/libs/gst/base/meson.build b/libs/gst/base/meson.build deleted file mode 100644 index d289376133..0000000000 --- a/libs/gst/base/meson.build +++ /dev/null @@ -1,107 +0,0 @@ -gst_base_sources = [ - 'gstadapter.c', - 'gstaggregator.c', - 'gstbaseparse.c', - 'gstbasesink.c', - 'gstbasesrc.c', - 'gstbasetransform.c', - 'gstbitreader.c', - 'gstbitwriter.c', - 'gstbytereader.c', - 'gstbytewriter.c', - 'gstcollectpads.c', - 'gstdataqueue.c', - 'gstflowcombiner.c', - 'gstpushsrc.c', - 'gstqueuearray.c', - 'gsttypefindhelper.c', -] - -gst_base_headers = [ - 'base.h', - 'base-prelude.h', - 'gstadapter.h', - 'gstaggregator.h', - 'gstbaseparse.h', - 'gstbasesink.h', - 'gstbasesrc.h', - 'gstbasetransform.h', - 'gstbitreader.h', - 'gstbitwriter.h', - 'gstbytereader.h', - 'gstbytewriter.h', - 'gstcollectpads.h', - 'gstdataqueue.h', - 'gstflowcombiner.h', - 'gstpushsrc.h', - 'gstqueuearray.h', - 'gsttypefindhelper.h', -] - -gst_base_gen_sources = [] - -gst_base = library('gstbase-@0@'.format(apiversion), - gst_base_sources, - c_args : gst_c_args + ['-DBUILDING_GST_BASE'], - version : libversion, - soversion : soversion, - darwin_versions : osxversion, - install : true, - include_directories : [configinc, libsinc], - dependencies : [gobject_dep, glib_dep, gst_dep], -) - -pkgconfig.generate(gst_base, - libraries : [libgst], - variables : pkgconfig_variables, - subdirs : pkgconfig_subdirs, - name : 'gstreamer-base-1.0', - description : 'Base classes for GStreamer elements', -) - -if build_gir - gst_gir_extra_args = gir_init_section + [ '--c-include=gst/base/base.h' ] - gst_base_gir = gnome.generate_gir(gst_base, - sources : gst_base_sources + gst_base_headers, - namespace : 'GstBase', - nsversion : apiversion, - identifier_prefix : 'Gst', - symbol_prefix : 'gst', - export_packages : 'gstreamer-base-1.0', - dependencies : [gst_dep], - include_directories : [configinc, libsinc, privinc], - includes : ['GLib-2.0', 'GObject-2.0', 'GModule-2.0', 'Gst-1.0'], - install : true, - extra_args : gst_gir_extra_args, - ) - - gst_base_gen_sources += [gst_base_gir] -endif - -gst_base_dep = declare_dependency(link_with : gst_base, - include_directories : [libsinc], - dependencies : [gst_dep], - sources : gst_base_gen_sources) - -meson.override_dependency('gstreamer-base-1.0', gst_base_dep) - -install_headers('base.h', - 'base-prelude.h', - 'gstadapter.h', - 'gstaggregator.h', - 'gstbaseparse.h', - 'gstbasesink.h', - 'gstbasesrc.h', - 'gstbasetransform.h', - 'gstbitreader.h', - 'gstbitwriter.h', - 'gstbytereader.h', - 'gstbytewriter.h', - 'gstcollectpads.h', - 'gstdataqueue.h', - 'gstflowcombiner.h', - 'gstpushsrc.h', - 'gstqueuearray.h', - 'gsttypefindhelper.h', - subdir : 'gstreamer-1.0/gst/base', -) diff --git a/libs/gst/check/check-prelude.h b/libs/gst/check/check-prelude.h deleted file mode 100644 index 0fc272598b..0000000000 --- a/libs/gst/check/check-prelude.h +++ /dev/null @@ -1,43 +0,0 @@ -/* GStreamer Check Library - * Copyright (C) 2018 GStreamer developers - * - * check-prelude.h: prelude include header for gst-check library - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_CHECK_PRELUDE_H__ -#define __GST_CHECK_PRELUDE_H__ - -#include <gst/gst.h> - -#ifndef GST_CHECK_API -#ifdef BUILDING_GST_CHECK -#define GST_CHECK_API GST_API_EXPORT /* from config.h */ -#else -#define GST_CHECK_API GST_API_IMPORT -#endif -#endif - -#ifndef GST_DISABLE_DEPRECATED -#define GST_CHECK_DEPRECATED GST_CHECK_API -#define GST_CHECK_DEPRECATED_FOR(f) GST_CHECK_API -#else -#define GST_CHECK_DEPRECATED G_DEPRECATED GST_CHECK_API -#define GST_CHECK_DEPRECATED_FOR(f) G_DEPRECATED_FOR(f) GST_CHECK_API -#endif - -#endif /* __GST_CHECK_PRELUDE_H__ */ diff --git a/libs/gst/check/check.h b/libs/gst/check/check.h deleted file mode 100644 index 512f06ca54..0000000000 --- a/libs/gst/check/check.h +++ /dev/null @@ -1,33 +0,0 @@ -/* GStreamer - * Copyright (C) 2012 GStreamer developers - * - * check.h: single include header for gst-check library - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_CHECK__H__ -#define __GST_CHECK__H__ - -#include <gst/check/check-prelude.h> - -#include <gst/check/gstbufferstraw.h> -#include <gst/check/gstcheck.h> -#include <gst/check/gstconsistencychecker.h> -#include <gst/check/gstharness.h> -#include <gst/check/gsttestclock.h> - -#endif /* __GST_CHECK__H__ */ diff --git a/libs/gst/check/gstbufferstraw.c b/libs/gst/check/gstbufferstraw.c deleted file mode 100644 index 893ae6a4f1..0000000000 --- a/libs/gst/check/gstbufferstraw.c +++ /dev/null @@ -1,173 +0,0 @@ -/* GStreamer - * - * unit testing helper lib - * - * Copyright (C) 2006 Andy Wingo <wingo at pobox.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * SECTION:gstcheckbufferstraw - * @title: GstBufferStraw - * @short_description: Buffer interception code for GStreamer unit tests - * - * These macros and functions are for internal use of the unit tests found - * inside the 'check' directories of various GStreamer packages. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstbufferstraw.h" - -static GCond cond; -static GMutex lock; -static GstBuffer *buf = NULL; -static gulong id; - -/* called for every buffer. Waits until the global "buf" variable is unset, - * then sets it to the buffer received, and signals. */ -static GstPadProbeReturn -buffer_probe (GstPad * pad, GstPadProbeInfo * info, gpointer unused) -{ - GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER (info); - - g_mutex_lock (&lock); - - while (buf != NULL) - g_cond_wait (&cond, &lock); - - /* increase the refcount because we store it globally for others to use */ - buf = gst_buffer_ref (buffer); - - g_cond_signal (&cond); - - g_mutex_unlock (&lock); - - return GST_PAD_PROBE_OK; -} - -/** - * gst_buffer_straw_start_pipeline: - * @bin: the pipeline to run - * @pad: a pad on an element in @bin - * - * Sets up a pipeline for buffer sucking. This will allow you to call - * gst_buffer_straw_get_buffer() to access buffers as they pass over @pad. - * - * This function is normally used in unit tests that want to verify that a - * particular element is outputting correct buffers. For example, you would make - * a pipeline via gst_parse_launch(), pull out the pad you want to monitor, then - * call gst_buffer_straw_get_buffer() to get the buffers that pass through @pad. - * The pipeline will block until you have sucked off the buffers. - * - * This function will set the state of @bin to PLAYING; to clean up, be sure to - * call gst_buffer_straw_stop_pipeline(). - * - * Note that you may not start two buffer straws at the same time. This function - * is intended for unit tests, not general API use. In fact it calls fail_if - * from libcheck, so you cannot use it outside unit tests. - */ -void -gst_buffer_straw_start_pipeline (GstElement * bin, GstPad * pad) -{ - GstStateChangeReturn ret; - - id = gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER, - buffer_probe, NULL, NULL); - - ret = gst_element_set_state (bin, GST_STATE_PLAYING); - fail_if (ret == GST_STATE_CHANGE_FAILURE, "Could not start test pipeline"); - if (ret == GST_STATE_CHANGE_ASYNC) { - ret = gst_element_get_state (bin, NULL, NULL, GST_CLOCK_TIME_NONE); - fail_if (ret != GST_STATE_CHANGE_SUCCESS, "Could not start test pipeline"); - } -} - -/** - * gst_buffer_straw_get_buffer: - * @bin: the pipeline previously started via gst_buffer_straw_start_pipeline() - * @pad: the pad previously passed to gst_buffer_straw_start_pipeline() - * - * Get one buffer from @pad. Implemented via buffer probes. This function will - * block until the pipeline passes a buffer over @pad, so for robust behavior - * in unit tests, you need to use check's timeout to fail out in the case that a - * buffer never arrives. - * - * You must have previously called gst_buffer_straw_start_pipeline() on - * @pipeline and @pad. - * - * Returns: the captured #GstBuffer. - */ -GstBuffer * -gst_buffer_straw_get_buffer (GstElement * bin, GstPad * pad) -{ - GstBuffer *ret; - - g_mutex_lock (&lock); - - while (buf == NULL) - g_cond_wait (&cond, &lock); - - ret = buf; - buf = NULL; - - g_cond_signal (&cond); - - g_mutex_unlock (&lock); - - return ret; -} - -/** - * gst_buffer_straw_stop_pipeline: - * @bin: the pipeline previously started via gst_buffer_straw_start_pipeline() - * @pad: the pad previously passed to gst_buffer_straw_start_pipeline() - * - * Set @bin to #GST_STATE_NULL and release resource allocated in - * gst_buffer_straw_start_pipeline(). - * - * You must have previously called gst_buffer_straw_start_pipeline() on - * @pipeline and @pad. - */ -void -gst_buffer_straw_stop_pipeline (GstElement * bin, GstPad * pad) -{ - GstStateChangeReturn ret; - - g_mutex_lock (&lock); - if (buf) - gst_buffer_unref (buf); - buf = NULL; - gst_pad_remove_probe (pad, (guint) id); - id = 0; - g_cond_signal (&cond); - g_mutex_unlock (&lock); - - ret = gst_element_set_state (bin, GST_STATE_NULL); - fail_if (ret == GST_STATE_CHANGE_FAILURE, "Could not stop test pipeline"); - if (ret == GST_STATE_CHANGE_ASYNC) { - ret = gst_element_get_state (bin, NULL, NULL, GST_CLOCK_TIME_NONE); - fail_if (ret != GST_STATE_CHANGE_SUCCESS, "Could not stop test pipeline"); - } - - g_mutex_lock (&lock); - if (buf) - gst_buffer_unref (buf); - buf = NULL; - g_mutex_unlock (&lock); -} diff --git a/libs/gst/check/gstbufferstraw.h b/libs/gst/check/gstbufferstraw.h deleted file mode 100644 index 9eec1f9bd4..0000000000 --- a/libs/gst/check/gstbufferstraw.h +++ /dev/null @@ -1,40 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2006 Andy Wingo <wingo at pobox.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_BUFFER_STRAW_H__ -#define __GST_BUFFER_STRAW_H__ - - -#include <gst/check/gstcheck.h> - -G_BEGIN_DECLS - -GST_CHECK_API -void gst_buffer_straw_start_pipeline (GstElement * bin, GstPad * pad); - -GST_CHECK_API -GstBuffer * gst_buffer_straw_get_buffer (GstElement * bin, GstPad * pad); - -GST_CHECK_API -void gst_buffer_straw_stop_pipeline (GstElement * bin, GstPad * pad); - -G_END_DECLS - -#endif /* __GST_BUFFER_STRAW_H__ */ diff --git a/libs/gst/check/gstcheck.c b/libs/gst/check/gstcheck.c deleted file mode 100644 index 68b4cd3df2..0000000000 --- a/libs/gst/check/gstcheck.c +++ /dev/null @@ -1,1306 +0,0 @@ -/* GStreamer - * - * Common code for GStreamer unittests - * - * Copyright (C) 2004,2006 Thomas Vander Stichele <thomas at apestaart dot org> - * Copyright (C) 2008 Thijs Vermeir <thijsvermeir@gmail.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ -/** - * SECTION:gstcheck - * @title: GstCheck - * @short_description: Common code for GStreamer unit tests - * - * These macros and functions are for internal use of the unit tests found - * inside the 'check' directories of various GStreamer packages. - * - * One notable feature is that one can use the environment variables GST_CHECKS - * and GST_CHECKS_IGNORE to select which tests to run or skip. Both variables - * can contain a comma separated list of test name globs (e.g. test_*). - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstcheck.h" - -GST_DEBUG_CATEGORY (check_debug); - -/* logging function for tests - * a test uses g_message() to log a debug line - * a gst unit test can be run with GST_TEST_DEBUG env var set to see the - * messages - */ - -gboolean _gst_check_threads_running = FALSE; -GList *thread_list = NULL; -GMutex mutex; -GCond start_cond; /* used to notify main thread of thread startups */ -GCond sync_cond; /* used to synchronize all threads and main thread */ - -GList *buffers = NULL; -GMutex check_mutex; -GCond check_cond; - -/* FIXME 2.0: shouldn't _gst_check_debug be static? Not used anywhere */ -gboolean _gst_check_debug = FALSE; -gboolean _gst_check_raised_critical = FALSE; -gboolean _gst_check_raised_warning = FALSE; -gboolean _gst_check_expecting_log = FALSE; -gboolean _gst_check_list_tests = FALSE; -static GQueue _gst_check_log_filters = G_QUEUE_INIT; -static GMutex _gst_check_log_filters_mutex; - -struct _GstCheckLogFilter -{ - gchar *log_domain; - GLogLevelFlags log_level; - GRegex *regex; - GstCheckLogFilterFunc func; - gpointer user_data; - GDestroyNotify destroy; -}; - - -static gboolean -gst_check_match_log_filter (const GstCheckLogFilter * filter, - const gchar * log_domain, GLogLevelFlags log_level, const gchar * message) -{ - if (g_strcmp0 (log_domain, filter->log_domain) != 0) - return FALSE; - - if ((log_level & filter->log_level) == 0) - return FALSE; - - if (!g_regex_match (filter->regex, message, 0, NULL)) - return FALSE; - - return TRUE; -} - -static GstCheckLogFilter * -gst_check_alloc_log_filter (const gchar * log_domain, GLogLevelFlags log_level, - GRegex * regex, GstCheckLogFilterFunc func, gpointer user_data, - GDestroyNotify destroy_data) -{ - GstCheckLogFilter *filter; - - filter = g_slice_new (GstCheckLogFilter); - filter->log_domain = g_strdup (log_domain); - filter->log_level = log_level; - filter->regex = regex; - filter->func = func; - filter->user_data = user_data; - filter->destroy = destroy_data; - - return filter; -} - -static void -gst_check_free_log_filter (GstCheckLogFilter * filter) -{ - if (!filter) - return; - - g_free (filter->log_domain); - g_regex_unref (filter->regex); - if (filter->destroy) - filter->destroy (filter->user_data); - g_slice_free (GstCheckLogFilter, filter); -} - - -/** - * gst_check_add_log_filter: (skip) - * @log_domain: the log domain of the message - * @log_level: the log level of the message - * @regex: (transfer full): a #GRegex to match the message - * @func: the function to call for matching messages - * @user_data: the user data to pass to @func - * @destroy_data: #GDestroyNotify for @user_data - * - * Add a callback @func to be called for all log messages that matches - * @log_domain, @log_level and @regex. If @func is NULL the - * matching logs will be silently discarded by GstCheck. - * - * MT safe. - * - * Returns: A filter that can be passed to @gst_check_remove_log_filter. - * - * Since: 1.12 - **/ -GstCheckLogFilter * -gst_check_add_log_filter (const gchar * log_domain, GLogLevelFlags log_level, - GRegex * regex, GstCheckLogFilterFunc func, gpointer user_data, - GDestroyNotify destroy_data) -{ - GstCheckLogFilter *filter; - - g_return_val_if_fail (regex != NULL, NULL); - - filter = gst_check_alloc_log_filter (log_domain, log_level, regex, - func, user_data, destroy_data); - g_mutex_lock (&_gst_check_log_filters_mutex); - g_queue_push_tail (&_gst_check_log_filters, filter); - g_mutex_unlock (&_gst_check_log_filters_mutex); - - return filter; -} - -/** - * gst_check_remove_log_filter: - * @filter: Filter returned by @gst_check_add_log_filter - * - * Remove a filter that has been added by @gst_check_add_log_filter. - * - * MT safe. - * - * Since: 1.12 - **/ -void -gst_check_remove_log_filter (GstCheckLogFilter * filter) -{ - g_mutex_lock (&_gst_check_log_filters_mutex); - g_queue_remove (&_gst_check_log_filters, filter); - gst_check_free_log_filter (filter); - g_mutex_unlock (&_gst_check_log_filters_mutex); -} - -/** - * gst_check_clear_log_filter: - * - * Clear all filters added by @gst_check_add_log_filter. - * - * MT safe. - * - * Since: 1.12 - **/ -void -gst_check_clear_log_filter (void) -{ - g_mutex_lock (&_gst_check_log_filters_mutex); - g_queue_foreach (&_gst_check_log_filters, - (GFunc) gst_check_free_log_filter, NULL); - g_queue_clear (&_gst_check_log_filters); - g_mutex_unlock (&_gst_check_log_filters_mutex); -} - -typedef struct -{ - const gchar *domain; - const gchar *message; - GLogLevelFlags level; - gboolean discard; -} LogFilterApplyData; - -static void -gst_check_apply_log_filter (GstCheckLogFilter * filter, - LogFilterApplyData * data) -{ - if (gst_check_match_log_filter (filter, data->domain, data->level, - data->message)) { - if (filter->func) - data->discard |= filter->func (data->domain, data->level, - data->message, filter->user_data); - else - data->discard = TRUE; - } -} - -static gboolean -gst_check_filter_log_filter (const gchar * log_domain, - GLogLevelFlags log_level, const gchar * message) -{ - LogFilterApplyData data; - - data.domain = log_domain; - data.level = log_level; - data.message = message; - data.discard = FALSE; - - g_mutex_lock (&_gst_check_log_filters_mutex); - g_queue_foreach (&_gst_check_log_filters, (GFunc) gst_check_apply_log_filter, - &data); - g_mutex_unlock (&_gst_check_log_filters_mutex); - - if (data.discard) - GST_DEBUG ("Discarding message: %s", message); - - return data.discard; -} - -static gboolean -gst_check_log_fatal_func (const gchar * log_domain, GLogLevelFlags log_level, - const gchar * message, gpointer user_data) -{ - if (gst_check_filter_log_filter (log_domain, log_level, message)) - return FALSE; - - return TRUE; -} - - -static void gst_check_log_message_func - (const gchar * log_domain, GLogLevelFlags log_level, - const gchar * message, gpointer user_data) -{ - if (gst_check_filter_log_filter (log_domain, log_level, message)) - return; - - if (_gst_check_debug) { - g_print ("%s\n", message); - } -} - -static void gst_check_log_critical_func - (const gchar * log_domain, GLogLevelFlags log_level, - const gchar * message, gpointer user_data) -{ - if (gst_check_filter_log_filter (log_domain, log_level, message)) - return; - - if (!_gst_check_expecting_log) { - gchar *trace; - - g_print ("\n\nUnexpected critical/warning: %s\n", message); - - trace = gst_debug_get_stack_trace (GST_STACK_TRACE_SHOW_FULL); - if (trace) { - g_print ("\nStack trace:\n%s\n", trace); - g_free (trace); - } - fail ("Unexpected critical/warning: %s", message); - } - - if (_gst_check_debug) { - g_print ("\nExpected critical/warning: %s\n", message); - } - - if (log_level & G_LOG_LEVEL_CRITICAL) - _gst_check_raised_critical = TRUE; - if (log_level & G_LOG_LEVEL_WARNING) - _gst_check_raised_warning = TRUE; -} - -static gint -sort_plugins (GstPlugin * a, GstPlugin * b) -{ - int ret; - - ret = strcmp (gst_plugin_get_source (a), gst_plugin_get_source (b)); - if (ret == 0) - ret = strcmp (gst_plugin_get_name (a), gst_plugin_get_name (b)); - return ret; -} - -static void -print_plugins (void) -{ - GList *plugins, *l; - - plugins = gst_registry_get_plugin_list (gst_registry_get ()); - plugins = g_list_sort (plugins, (GCompareFunc) sort_plugins); - for (l = plugins; l != NULL; l = l->next) { - GstPlugin *plugin = GST_PLUGIN (l->data); - - if (strcmp (gst_plugin_get_source (plugin), "BLACKLIST") != 0) { - GST_LOG ("%20s@%s", gst_plugin_get_name (plugin), - GST_STR_NULL (gst_plugin_get_filename (plugin))); - } - } - gst_plugin_list_free (plugins); -} - -static void -gst_check_deinit (void) -{ - gst_deinit (); - gst_check_clear_log_filter (); -} - -/* gst_check_init: - * @argc: (inout) (allow-none): pointer to application's argc - * @argv: (inout) (array length=argc) (allow-none): pointer to application's argv - * - * Initialize GStreamer testing - * - * NOTE: Needs to be called before creating the testsuite - * so that the tests can be listed. - * */ -void -gst_check_init (int *argc, char **argv[]) -{ - guint timeout_multiplier = 1; - GOptionContext *ctx; - GError *err = NULL; - GOptionEntry options[] = { - {"list-tests", 'l', 0, G_OPTION_ARG_NONE, &_gst_check_list_tests, - "List tests present in the testsuite", NULL}, - {NULL} - }; - - ctx = g_option_context_new ("gst-check"); - g_option_context_add_main_entries (ctx, options, NULL); - g_option_context_add_group (ctx, gst_init_get_option_group ()); - - if (!g_option_context_parse (ctx, argc, argv, &err)) { - if (err) - g_printerr ("Error initializing: %s\n", GST_STR_NULL (err->message)); - else - g_printerr ("Error initializing: Unknown error!\n"); - g_clear_error (&err); - } - g_option_context_free (ctx); - - GST_DEBUG_CATEGORY_INIT (check_debug, "check", 0, "check regression tests"); - - if (atexit (gst_check_deinit) != 0) { - GST_ERROR ("failed to set gst_check_deinit as exit function"); - } - - if (g_getenv ("GST_TEST_DEBUG")) - _gst_check_debug = TRUE; - - g_log_set_handler (NULL, G_LOG_LEVEL_MESSAGE, gst_check_log_message_func, - NULL); - g_log_set_handler (NULL, G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING, - gst_check_log_critical_func, NULL); - g_log_set_handler ("GStreamer", G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING, - gst_check_log_critical_func, NULL); - g_log_set_handler ("GLib-GObject", G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING, - gst_check_log_critical_func, NULL); - g_log_set_handler ("GLib-GIO", G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING, - gst_check_log_critical_func, NULL); - g_log_set_handler ("GLib", G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING, - gst_check_log_critical_func, NULL); - g_test_log_set_fatal_handler (gst_check_log_fatal_func, NULL); - - print_plugins (); - -#ifdef TARGET_CPU - GST_INFO ("target CPU: %s", TARGET_CPU); -#endif - -#ifdef HAVE_CPU_ARM - timeout_multiplier = 10; -#endif - - if (timeout_multiplier > 1) { - const gchar *tmult = g_getenv ("CK_TIMEOUT_MULTIPLIER"); - - if (tmult == NULL) { - gchar num_str[32]; - - g_snprintf (num_str, sizeof (num_str), "%d", timeout_multiplier); - GST_INFO ("slow CPU, setting CK_TIMEOUT_MULTIPLIER to %s", num_str); - g_setenv ("CK_TIMEOUT_MULTIPLIER", num_str, TRUE); - } else { - GST_INFO ("CK_TIMEOUT_MULTIPLIER already set to '%s'", tmult); - } - } -} - -/* message checking */ -void -gst_check_message_error (GstMessage * message, GstMessageType type, - GQuark domain, gint code) -{ - GError *error; - gchar *debug; - - fail_unless (GST_MESSAGE_TYPE (message) == type, - "message is of type %s instead of expected type %s", - gst_message_type_get_name (GST_MESSAGE_TYPE (message)), - gst_message_type_get_name (type)); - gst_message_parse_error (message, &error, &debug); - fail_unless_equals_int (error->domain, domain); - fail_unless_equals_int (error->code, code); - g_error_free (error); - g_free (debug); -} - -/* helper functions */ -/** - * gst_check_chain_func: - * - * A fake chain function that appends the buffer to the internal list of - * buffers. - */ -GstFlowReturn -gst_check_chain_func (GstPad * pad, GstObject * parent, GstBuffer * buffer) -{ - GST_DEBUG_OBJECT (pad, "chain_func: received buffer %p", buffer); - buffers = g_list_append (buffers, buffer); - - g_mutex_lock (&check_mutex); - g_cond_signal (&check_cond); - g_mutex_unlock (&check_mutex); - - return GST_FLOW_OK; -} - -/** - * gst_check_setup_element: - * @factory: factory - * - * setup an element for a filter test with mysrcpad and mysinkpad - * - * Returns: (transfer full): a new element - */ -GstElement * -gst_check_setup_element (const gchar * factory) -{ - GstElement *element; - - GST_DEBUG ("setup_element"); - - element = gst_element_factory_make (factory, factory); - fail_if (element == NULL, "Could not create a '%s' element", factory); - ASSERT_OBJECT_REFCOUNT (element, factory, 1); - return element; -} - -void -gst_check_teardown_element (GstElement * element) -{ - GST_DEBUG ("teardown_element"); - - fail_unless (gst_element_set_state (element, GST_STATE_NULL) == - GST_STATE_CHANGE_SUCCESS, "could not set to null"); - ASSERT_OBJECT_REFCOUNT (element, "element", 1); - gst_object_unref (element); -} - -/** - * gst_check_setup_src_pad: - * @element: element to setup pad on - * @tmpl: pad template - * - * Does the same as #gst_check_setup_src_pad_by_name with the <emphasis> name </emphasis> parameter equal to "sink". - * - * Returns: (transfer full): A new pad that can be used to inject data on @element - */ -GstPad * -gst_check_setup_src_pad (GstElement * element, GstStaticPadTemplate * tmpl) -{ - return gst_check_setup_src_pad_by_name (element, tmpl, "sink"); -} - -/** - * gst_check_setup_src_pad_by_name: - * @element: element to setup src pad on - * @tmpl: pad template - * @name: Name of the @element sink pad that will be linked to the src pad that will be setup - * - * Creates a new src pad (based on the given @tmpl) and links it to the given @element sink pad (the pad that matches the given @name). - * Before using the src pad to push data on @element you need to call #gst_check_setup_events on the created src pad. - * - * Example of how to push a buffer on @element: - * - * |[<!-- language="C" --> - * static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", - * GST_PAD_SINK, - * GST_PAD_ALWAYS, - * GST_STATIC_CAPS (YOUR_CAPS_TEMPLATE_STRING) - * ); - * static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", - * GST_PAD_SRC, - * GST_PAD_ALWAYS, - * GST_STATIC_CAPS (YOUR_CAPS_TEMPLATE_STRING) - * ); - * - * GstElement * element = gst_check_setup_element ("element"); - * GstPad * mysrcpad = gst_check_setup_src_pad (element, &srctemplate); - * GstPad * mysinkpad = gst_check_setup_sink_pad (element, &sinktemplate); - * - * gst_pad_set_active (mysrcpad, TRUE); - * gst_pad_set_active (mysinkpad, TRUE); - * fail_unless (gst_element_set_state (element, GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, "could not set to playing"); - * - * GstCaps * caps = gst_caps_from_string (YOUR_DESIRED_SINK_CAPS); - * gst_check_setup_events (mysrcpad, element, caps, GST_FORMAT_TIME); - * gst_caps_unref (caps); - * - * fail_unless (gst_pad_push (mysrcpad, gst_buffer_new_and_alloc(2)) == GST_FLOW_OK); - * ]| - * - * For very simple input/output test scenarios checkout #gst_check_element_push_buffer_list and #gst_check_element_push_buffer. - * - * Returns: (transfer full): A new pad that can be used to inject data on @element - */ -GstPad * -gst_check_setup_src_pad_by_name (GstElement * element, - GstStaticPadTemplate * tmpl, const gchar * name) -{ - GstPadTemplate *ptmpl = gst_static_pad_template_get (tmpl); - GstPad *srcpad; - - srcpad = gst_check_setup_src_pad_by_name_from_template (element, ptmpl, name); - - gst_object_unref (ptmpl); - - return srcpad; -} - -/** - * gst_check_setup_src_pad_from_template: - * @element: element to setup pad on - * @tmpl: pad template - * - * Returns: (transfer full): a new pad - * - * Since: 1.4 - */ -GstPad * -gst_check_setup_src_pad_from_template (GstElement * element, - GstPadTemplate * tmpl) -{ - return gst_check_setup_src_pad_by_name_from_template (element, tmpl, "sink"); -} - -/** - * gst_check_setup_src_pad_by_name_from_template: - * @element: element to setup pad on - * @tmpl: pad template - * @name: name - * - * Returns: (transfer full): a new pad - * - * Since: 1.4 - */ -GstPad * -gst_check_setup_src_pad_by_name_from_template (GstElement * element, - GstPadTemplate * tmpl, const gchar * name) -{ - GstPad *srcpad, *sinkpad; - - /* sending pad */ - srcpad = gst_pad_new_from_template (tmpl, "src"); - GST_DEBUG_OBJECT (element, "setting up sending pad %p", srcpad); - fail_if (srcpad == NULL, "Could not create a srcpad"); - ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 1); - - sinkpad = gst_element_get_static_pad (element, name); - if (sinkpad == NULL) - sinkpad = gst_element_request_pad_simple (element, name); - fail_if (sinkpad == NULL, "Could not get sink pad from %s", - GST_ELEMENT_NAME (element)); - fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK, - "Could not link source and %s sink pads", GST_ELEMENT_NAME (element)); - gst_object_unref (sinkpad); /* because we got it higher up */ - - return srcpad; -} - -void -gst_check_teardown_pad_by_name (GstElement * element, const gchar * name) -{ - GstPad *pad_peer, *pad_element; - - /* clean up floating src pad */ - pad_element = gst_element_get_static_pad (element, name); - /* We don't check the refcount here since there *might* be - * a pad cache holding an extra reference on pad_element. - * To get to a state where no pad cache will exist, - * we first unlink that pad. */ - pad_peer = gst_pad_get_peer (pad_element); - - if (pad_peer) { - if (gst_pad_get_direction (pad_element) == GST_PAD_SINK) - gst_pad_unlink (pad_peer, pad_element); - else - gst_pad_unlink (pad_element, pad_peer); - } - - gst_object_unref (pad_element); - - if (pad_peer) { - gst_object_unref (pad_peer); - gst_object_unref (pad_peer); - } -} - -void -gst_check_teardown_src_pad (GstElement * element) -{ - gst_check_teardown_pad_by_name (element, "sink"); -} - -/** - * gst_check_setup_sink_pad: - * @element: element to setup pad on - * @tmpl: pad template - * - * Does the same as #gst_check_setup_sink_pad_by_name with the <emphasis> name </emphasis> parameter equal to "src". - * - * Returns: (transfer full): a new pad that can be used to check the output of @element - */ -GstPad * -gst_check_setup_sink_pad (GstElement * element, GstStaticPadTemplate * tmpl) -{ - return gst_check_setup_sink_pad_by_name (element, tmpl, "src"); -} - -/** - * gst_check_setup_sink_pad_by_name: - * @element: element to setup pad on - * @tmpl: pad template - * @name: Name of the @element src pad that will be linked to the sink pad that will be setup - * - * Creates a new sink pad (based on the given @tmpl) and links it to the given @element src pad - * (the pad that matches the given @name). - * You can set event/chain/query functions on this pad to check the output of the @element. - * - * Returns: (transfer full): a new pad that can be used to check the output of @element - */ -GstPad * -gst_check_setup_sink_pad_by_name (GstElement * element, - GstStaticPadTemplate * tmpl, const gchar * name) -{ - GstPadTemplate *ptmpl = gst_static_pad_template_get (tmpl); - GstPad *sinkpad; - - sinkpad = - gst_check_setup_sink_pad_by_name_from_template (element, ptmpl, name); - - gst_object_unref (ptmpl); - - return sinkpad; -} - -/** - * gst_check_setup_sink_pad_from_template: - * @element: element to setup pad on - * @tmpl: pad template - * - * Returns: (transfer full): a new pad - * - * Since: 1.4 - */ -GstPad * -gst_check_setup_sink_pad_from_template (GstElement * element, - GstPadTemplate * tmpl) -{ - return gst_check_setup_sink_pad_by_name_from_template (element, tmpl, "src"); -} - -/** - * gst_check_setup_sink_pad_by_name_from_template: - * @element: element to setup pad on - * @tmpl: pad template - * @name: name - * - * Returns: (transfer full): a new pad - * - * Since: 1.4 - */ -GstPad * -gst_check_setup_sink_pad_by_name_from_template (GstElement * element, - GstPadTemplate * tmpl, const gchar * name) -{ - GstPad *srcpad, *sinkpad; - - /* receiving pad */ - sinkpad = gst_pad_new_from_template (tmpl, "sink"); - GST_DEBUG_OBJECT (element, "setting up receiving pad %p", sinkpad); - fail_if (sinkpad == NULL, "Could not create a sinkpad"); - - srcpad = gst_element_get_static_pad (element, name); - if (srcpad == NULL) - srcpad = gst_element_request_pad_simple (element, name); - fail_if (srcpad == NULL, "Could not get source pad from %s", - GST_ELEMENT_NAME (element)); - gst_pad_set_chain_function (sinkpad, gst_check_chain_func); - - GST_DEBUG_OBJECT (element, "Linking element src pad and receiving sink pad"); - fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK, - "Could not link %s source and sink pads", GST_ELEMENT_NAME (element)); - gst_object_unref (srcpad); /* because we got it higher up */ - - GST_DEBUG_OBJECT (element, "set up srcpad"); - return sinkpad; -} - -void -gst_check_teardown_sink_pad (GstElement * element) -{ - gst_check_teardown_pad_by_name (element, "src"); -} - -/** - * gst_check_drop_buffers: - * - * Unref and remove all buffers that are in the global @buffers GList, - * emptying the list. - */ -void -gst_check_drop_buffers (void) -{ - while (buffers != NULL) { - gst_buffer_unref (GST_BUFFER (buffers->data)); - buffers = g_list_delete_link (buffers, buffers); - } -} - -/** - * gst_check_caps_equal: - * @caps1: first caps to compare - * @caps2: second caps to compare - * - * Compare two caps with gst_caps_is_equal and fail unless they are - * equal. - */ -void -gst_check_caps_equal (GstCaps * caps1, GstCaps * caps2) -{ - gchar *name1 = gst_caps_to_string (caps1); - gchar *name2 = gst_caps_to_string (caps2); - - fail_unless (gst_caps_is_equal (caps1, caps2), - "caps ('%s') is not equal to caps ('%s')", name1, name2); - g_free (name1); - g_free (name2); -} - - -/** - * gst_check_buffer_data: - * @buffer: buffer to compare - * @data: data to compare to - * @size: size of data to compare - * - * Compare the buffer contents with @data and @size. - */ -void -gst_check_buffer_data (GstBuffer * buffer, gconstpointer data, gsize size) -{ - GstMapInfo info; - - fail_unless (gst_buffer_map (buffer, &info, GST_MAP_READ)); - GST_MEMDUMP ("Converted data", info.data, info.size); - GST_MEMDUMP ("Expected data", data, size); - if (info.size != size) { - fail ("buffer sizes not equal: expected %" G_GSIZE_FORMAT " got %" - G_GSIZE_FORMAT, size, info.size); - } - if (memcmp (info.data, data, size) != 0) { - g_print ("\nConverted data:\n"); - gst_util_dump_mem (info.data, info.size); - g_print ("\nExpected data:\n"); - gst_util_dump_mem (data, size); - fail ("buffer contents not equal"); - } - gst_buffer_unmap (buffer, &info); -} - -static gboolean -buffer_event_function (GstPad * pad, GstObject * noparent, GstEvent * event) -{ - if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) { - GstCaps *event_caps; - GstCaps *expected_caps = gst_pad_get_element_private (pad); - - gst_event_parse_caps (event, &event_caps); - fail_unless (gst_caps_is_fixed (expected_caps)); - fail_unless (gst_caps_is_fixed (event_caps)); - fail_unless (gst_caps_is_equal_fixed (event_caps, expected_caps)); - gst_event_unref (event); - return TRUE; - } - - return gst_pad_event_default (pad, noparent, event); -} - -/** - * gst_check_element_push_buffer_list: - * @element_name: name of the element that needs to be created - * @buffer_in: (element-type GstBuffer) (transfer full): a list of buffers that needs to be - * pushed to the element - * @caps_in: the #GstCaps expected of the sinkpad of the element - * @buffer_out: (element-type GstBuffer) (transfer full): a list of buffers that we expect from - * the element - * @caps_out: the #GstCaps expected of the srcpad of the element - * @last_flow_return: the last buffer push needs to give this GstFlowReturn - * - * Create an element using the factory providing the @element_name and push the - * buffers in @buffer_in to this element. The element should create the buffers - * equal to the buffers in @buffer_out. We only check the size and the data of - * the buffers. This function unrefs the buffers in the two lists. - * The last_flow_return parameter indicates the expected flow return value from - * pushing the final buffer in the list. - * This can be used to set up a test which pushes some buffers and then an - * invalid buffer, when the final buffer is expected to fail, for example. - */ -/* FIXME 2.0: rename this function now that there's GstBufferList? */ -void -gst_check_element_push_buffer_list (const gchar * element_name, - GList * buffer_in, GstCaps * caps_in, GList * buffer_out, - GstCaps * caps_out, GstFlowReturn last_flow_return) -{ - GstElement *element; - GstPad *pad_peer; - GstPad *sink_pad = NULL; - GstPad *src_pad; - GstBuffer *buffer; - - /* check that there are no buffers waiting */ - gst_check_drop_buffers (); - /* create the element */ - element = gst_check_setup_element (element_name); - fail_if (element == NULL, "failed to create the element '%s'", element_name); - fail_unless (GST_IS_ELEMENT (element), "the element is no element"); - /* create the src pad */ - buffer = GST_BUFFER (buffer_in->data); - - fail_unless (GST_IS_BUFFER (buffer), "There should be a buffer in buffer_in"); - src_pad = gst_pad_new ("src", GST_PAD_SRC); - if (caps_in) { - fail_unless (gst_caps_is_fixed (caps_in)); - gst_pad_use_fixed_caps (src_pad); - } - /* activate the pad */ - gst_pad_set_active (src_pad, TRUE); - GST_DEBUG ("src pad activated"); - gst_check_setup_events (src_pad, element, caps_in, GST_FORMAT_BYTES); - pad_peer = gst_element_get_static_pad (element, "sink"); - fail_if (pad_peer == NULL); - fail_unless (gst_pad_link (src_pad, pad_peer) == GST_PAD_LINK_OK, - "Could not link source and %s sink pads", GST_ELEMENT_NAME (element)); - gst_object_unref (pad_peer); - /* don't create the sink_pad if there is no buffer_out list */ - if (buffer_out != NULL) { - - GST_DEBUG ("buffer out detected, creating the sink pad"); - /* get the sink caps */ - if (caps_out) { - gchar *temp; - - fail_unless (gst_caps_is_fixed (caps_out)); - temp = gst_caps_to_string (caps_out); - - GST_DEBUG ("sink caps requested by buffer out: '%s'", temp); - g_free (temp); - } - - /* get the sink pad */ - sink_pad = gst_pad_new ("sink", GST_PAD_SINK); - fail_unless (GST_IS_PAD (sink_pad)); - /* configure the sink pad */ - gst_pad_set_chain_function (sink_pad, gst_check_chain_func); - gst_pad_set_active (sink_pad, TRUE); - if (caps_out) { - gst_pad_set_element_private (sink_pad, caps_out); - gst_pad_set_event_function (sink_pad, buffer_event_function); - } - /* get the peer pad */ - pad_peer = gst_element_get_static_pad (element, "src"); - fail_unless (gst_pad_link (pad_peer, sink_pad) == GST_PAD_LINK_OK, - "Could not link sink and %s source pads", GST_ELEMENT_NAME (element)); - gst_object_unref (pad_peer); - } - fail_unless (gst_element_set_state (element, - GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, - "could not set to playing"); - /* push all the buffers in the buffer_in list */ - fail_unless (g_list_length (buffer_in) > 0, "the buffer_in list is empty"); - while (buffer_in != NULL) { - GstBuffer *next_buffer = GST_BUFFER (buffer_in->data); - - fail_unless (GST_IS_BUFFER (next_buffer), - "data in buffer_in should be a buffer"); - /* remove the buffer from the list */ - buffer_in = g_list_remove (buffer_in, next_buffer); - if (buffer_in == NULL) { - fail_unless (gst_pad_push (src_pad, next_buffer) == last_flow_return, - "we expect something else from the last buffer"); - } else { - fail_unless (gst_pad_push (src_pad, next_buffer) == GST_FLOW_OK, - "Failed to push buffer in"); - } - } - fail_unless (gst_element_set_state (element, - GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null"); - /* check that there is a buffer out */ - fail_unless_equals_int (g_list_length (buffers), g_list_length (buffer_out)); - while (buffers != NULL) { - GstBuffer *new = GST_BUFFER (buffers->data); - GstBuffer *orig = GST_BUFFER (buffer_out->data); - GstMapInfo newinfo, originfo; - - fail_unless (gst_buffer_map (new, &newinfo, GST_MAP_READ)); - fail_unless (gst_buffer_map (orig, &originfo, GST_MAP_READ)); - - GST_LOG ("orig buffer: size %" G_GSIZE_FORMAT, originfo.size); - GST_LOG ("new buffer: size %" G_GSIZE_FORMAT, newinfo.size); - GST_MEMDUMP ("orig buffer", originfo.data, originfo.size); - GST_MEMDUMP ("new buffer", newinfo.data, newinfo.size); - - /* remove the buffers */ - buffers = g_list_remove (buffers, new); - buffer_out = g_list_remove (buffer_out, orig); - - fail_unless (originfo.size == newinfo.size, - "size of the buffers are not the same"); - fail_unless (memcmp (originfo.data, newinfo.data, newinfo.size) == 0, - "data is not the same"); -#if 0 - gst_check_caps_equal (GST_BUFFER_CAPS (orig), GST_BUFFER_CAPS (new)); -#endif - - gst_buffer_unmap (orig, &originfo); - gst_buffer_unmap (new, &newinfo); - - gst_buffer_unref (new); - gst_buffer_unref (orig); - } - /* teardown the element and pads */ - gst_pad_set_active (src_pad, FALSE); - gst_check_teardown_src_pad (element); - gst_pad_set_active (sink_pad, FALSE); - gst_check_teardown_sink_pad (element); - gst_check_teardown_element (element); -} - -/** - * gst_check_element_push_buffer: - * @element_name: name of the element that needs to be created - * @buffer_in: push this buffer to the element - * @caps_in: the #GstCaps expected of the sinkpad of the element - * @buffer_out: compare the result with this buffer - * @caps_out: the #GstCaps expected of the srcpad of the element - * - * Create an element using the factory providing the @element_name and - * push the @buffer_in to this element. The element should create one buffer - * and this will be compared with @buffer_out. We only check the caps - * and the data of the buffers. This function unrefs the buffers. - */ -void -gst_check_element_push_buffer (const gchar * element_name, - GstBuffer * buffer_in, GstCaps * caps_in, GstBuffer * buffer_out, - GstCaps * caps_out) -{ - GList *in = NULL; - GList *out = NULL; - - in = g_list_append (in, buffer_in); - out = g_list_append (out, buffer_out); - - gst_check_element_push_buffer_list (element_name, in, caps_in, out, caps_out, - GST_FLOW_OK); -} - -/** - * gst_check_abi_list: - * @list: A list of GstCheckABIStruct to be verified - * @have_abi_sizes: Whether there is a reference ABI size already specified, - * if it is %FALSE and the `GST_ABI` environment variable is set, usable code - * for @list will be printed. - * - * Verifies that reference values and current values are equals in @list. - */ -void -gst_check_abi_list (GstCheckABIStruct list[], gboolean have_abi_sizes) -{ - if (have_abi_sizes) { - gboolean ok = TRUE; - gint i; - - for (i = 0; list[i].name; i++) { - if (list[i].size != list[i].abi_size) { - ok = FALSE; - g_print ("sizeof(%s) is %d, expected %d\n", - list[i].name, list[i].size, list[i].abi_size); - } - } - fail_unless (ok, "failed ABI check"); - } else { - const gchar *fn; - - if ((fn = g_getenv ("GST_ABI"))) { - GError *err = NULL; - GString *s; - gint i; - - s = g_string_new ("\nGstCheckABIStruct list[] = {\n"); - for (i = 0; list[i].name; i++) { - g_string_append_printf (s, " {\"%s\", sizeof (%s), %d},\n", - list[i].name, list[i].name, list[i].size); - } - g_string_append (s, " {NULL, 0, 0}\n"); - g_string_append (s, "};\n"); - if (!g_file_set_contents (fn, s->str, s->len, &err)) { - g_print ("%s", s->str); - g_printerr ("\nFailed to write ABI information: %s\n", err->message); - g_clear_error (&err); - } else { - g_print ("\nWrote ABI information to '%s'.\n", fn); - } - g_string_free (s, TRUE); - } else { - g_print ("No structure size list was generated for this architecture.\n"); - g_print ("Run with GST_ABI environment variable set to output header.\n"); - } - } -} - -/** - * gst_check_run_suite: (skip) - * @suite: the check test suite - * @name: name - * @fname: file name - * - * Returns: number of failed tests - */ -gint -gst_check_run_suite (Suite * suite, const gchar * name, const gchar * fname) -{ - SRunner *sr; - gchar *xmlfilename = NULL; - gint nf; - GTimer *timer; - - sr = srunner_create (suite); - - if (g_getenv ("GST_CHECK_XML")) { - /* how lucky we are to have __FILE__ end in .c */ - xmlfilename = g_strdup_printf ("%sheck.xml", fname); - - srunner_set_xml (sr, xmlfilename); - } - - timer = g_timer_new (); - srunner_run_all (sr, CK_NORMAL); - nf = srunner_ntests_failed (sr); - g_print ("Check suite %s ran in %.3fs (tests failed: %d)\n", - name, g_timer_elapsed (timer, NULL), nf); - g_timer_destroy (timer); - g_free (xmlfilename); - srunner_free (sr); - g_thread_pool_stop_unused_threads (); - return nf; -} - -static gboolean -gst_check_have_checks_list (const gchar * env_var_name) -{ - const gchar *env_val; - - env_val = g_getenv (env_var_name); - return (env_val != NULL && *env_val != '\0'); -} - -static gboolean -gst_check_func_is_in_list (const gchar * env_var, const gchar * func_name) -{ - const gchar *gst_checks; - gboolean res = FALSE; - gchar **funcs, **f; - - gst_checks = g_getenv (env_var); - - if (gst_checks == NULL || *gst_checks == '\0') - return FALSE; - - /* only run specified functions */ - funcs = g_strsplit (gst_checks, ",", -1); - for (f = funcs; f != NULL && *f != NULL; ++f) { - if (g_pattern_match_simple (*f, func_name)) { - res = TRUE; - break; - } - } - g_strfreev (funcs); - return res; -} - -gboolean -_gst_check_run_test_func (const gchar * func_name) -{ - /* if we have a whitelist, run it only if it's in the whitelist */ - if (gst_check_have_checks_list ("GST_CHECKS")) - return gst_check_func_is_in_list ("GST_CHECKS", func_name); - - /* if we have a blacklist, run it only if it's not in the blacklist */ - if (gst_check_have_checks_list ("GST_CHECKS_IGNORE")) - return !gst_check_func_is_in_list ("GST_CHECKS_IGNORE", func_name); - - /* no filter specified => run all checks */ - return TRUE; -} - -/** - * gst_check_setup_events_with_stream_id: - * @srcpad: The src #GstPad to push on - * @element: The #GstElement use to create the stream id - * @caps: (allow-none): #GstCaps in case caps event must be sent - * @format: The #GstFormat of the default segment to send - * @stream_id: A unique identifier for the stream - * - * Push stream-start, caps and segment event, which consist of the minimum - * required events to allow streaming. Caps is optional to allow raw src - * testing. - */ -void -gst_check_setup_events_with_stream_id (GstPad * srcpad, GstElement * element, - GstCaps * caps, GstFormat format, const gchar * stream_id) -{ - GstSegment segment; - - gst_segment_init (&segment, format); - - fail_unless (gst_pad_push_event (srcpad, - gst_event_new_stream_start (stream_id))); - if (caps) - fail_unless (gst_pad_push_event (srcpad, gst_event_new_caps (caps))); - fail_unless (gst_pad_push_event (srcpad, gst_event_new_segment (&segment))); -} - -/** - * gst_check_setup_events: - * @srcpad: The src #GstPad to push on - * @element: The #GstElement use to create the stream id - * @caps: (allow-none): #GstCaps in case caps event must be sent - * @format: The #GstFormat of the default segment to send - * - * Push stream-start, caps and segment event, which consist of the minimum - * required events to allow streaming. Caps is optional to allow raw src - * testing. If @element has more than one src or sink pad, use - * gst_check_setup_events_with_stream_id() instead. - */ -void -gst_check_setup_events (GstPad * srcpad, GstElement * element, - GstCaps * caps, GstFormat format) -{ - gchar *stream_id; - - stream_id = gst_pad_create_stream_id (srcpad, element, NULL); - gst_check_setup_events_with_stream_id (srcpad, element, caps, format, - stream_id); - g_free (stream_id); -} - -typedef struct _DestroyedObjectStruct -{ - GObject *object; - gboolean destroyed; -} DestroyedObjectStruct; - -static void -weak_notify (DestroyedObjectStruct * destroyed, GObject ** object) -{ - destroyed->destroyed = TRUE; -} - -/** - * gst_check_objects_destroyed_on_unref: - * @object_to_unref: The #GObject to unref - * @first_object: (allow-none): The first object that should be destroyed as a - * concequence of unrefing @object_to_unref. - * @... : Additional object that should have been destroyed. - * - * Unrefs @object_to_unref and checks that is has properly been - * destroyed, also checks that the other objects passed in - * parameter have been destroyed as a concequence of - * unrefing @object_to_unref. Last variable argument should be NULL. - * - * Since: 1.6 - */ -void -gst_check_objects_destroyed_on_unref (gpointer object_to_unref, - gpointer first_object, ...) -{ - GObject *object; - GList *objs = NULL, *tmp; - DestroyedObjectStruct *destroyed = g_slice_new0 (DestroyedObjectStruct); - - destroyed->object = object_to_unref; - g_object_weak_ref (object_to_unref, (GWeakNotify) weak_notify, destroyed); - objs = g_list_prepend (objs, destroyed); - - if (first_object) { - va_list varargs; - - object = first_object; - - va_start (varargs, first_object); - while (object) { - destroyed = g_slice_new0 (DestroyedObjectStruct); - destroyed->object = object; - g_object_weak_ref (object, (GWeakNotify) weak_notify, destroyed); - objs = g_list_prepend (objs, destroyed); - object = va_arg (varargs, GObject *); - } - va_end (varargs); - } - gst_object_unref (object_to_unref); - - for (tmp = objs; tmp; tmp = tmp->next) { - DestroyedObjectStruct *destroyed = tmp->data; - - if (!destroyed->destroyed) { - fail_unless (destroyed->destroyed, - "%s_%p is not destroyed, %d refcounts left!", - GST_IS_OBJECT (destroyed-> - object) ? GST_OBJECT_NAME (destroyed->object) : - G_OBJECT_TYPE_NAME (destroyed), destroyed->object, - destroyed->object->ref_count); - } - g_slice_free (DestroyedObjectStruct, tmp->data); - } - g_list_free (objs); -} - -/** - * gst_check_object_destroyed_on_unref: - * @object_to_unref: The #GObject to unref - * - * Unrefs @object_to_unref and checks that is has properly been - * destroyed. - * - * Since: 1.6 - */ -void -gst_check_object_destroyed_on_unref (gpointer object_to_unref) -{ - gst_check_objects_destroyed_on_unref (object_to_unref, NULL, NULL); -} - -/* For ABI compatibility with GStreamer < 1.5 */ -/* *INDENT-OFF* */ -GST_CHECK_API void -_fail_unless (int result, const char *file, int line, const char *expr, ...) -G_GNUC_PRINTF (4, 5); -/* *INDENT-ON* */ - -void -_fail_unless (int result, const char *file, int line, const char *expr, ...) -{ - gchar *msg; - va_list args; - - if (result) { - _mark_point (file, line); - return; - } - - va_start (args, expr); - msg = g_strdup_vprintf (expr, args); - va_end (args); - - _ck_assert_failed (file, line, msg, NULL); - g_free (msg); -} diff --git a/libs/gst/check/gstcheck.h b/libs/gst/check/gstcheck.h deleted file mode 100644 index b7ec2c4ab3..0000000000 --- a/libs/gst/check/gstcheck.h +++ /dev/null @@ -1,756 +0,0 @@ -/* GStreamer - * - * Common code for GStreamer unittests - * - * Copyright (C) <2004> Thomas Vander Stichele <thomas at apestaart dot org> - * Copyright (C) <2008> Thijs Vermeir <thijsvermeir@gmail.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_CHECK_H__ -#define __GST_CHECK_H__ - -#include <signal.h> -#include <string.h> -#include <stdlib.h> -#include <math.h> - -#include <gst/gst.h> -#include <gst/check/check-prelude.h> - -#define CK_DLL_EXP GST_CHECK_API -#include <gst/check/internal-check.h> - -G_BEGIN_DECLS - -GST_CHECK_API GstDebugCategory *check_debug; -#define GST_CAT_DEFAULT check_debug - -/* logging function for tests - * a test uses g_message() to log a debug line - * a gst unit test can be run with GST_TEST_DEBUG env var set to see the - * messages - */ -GST_CHECK_API gboolean _gst_check_threads_running; -GST_CHECK_API gboolean _gst_check_raised_critical; -GST_CHECK_API gboolean _gst_check_raised_warning; -GST_CHECK_API gboolean _gst_check_expecting_log; -GST_CHECK_API gboolean _gst_check_list_tests; - -/* global variables used in test methods */ -GST_CHECK_API GList * buffers; - -GST_CHECK_API GMutex check_mutex; -GST_CHECK_API GCond check_cond; - -/** - * GstCheckABIStruct: - * @name: The name of the structure - * @size: The current size of a structure - * @abi_size: The reference size of the structure - */ -typedef struct -{ - const char *name; - int size; - int abi_size; -} -GstCheckABIStruct; - -/** - * GstCheckLogFilter: - * - * Opaque structure containing data about a log filter - * function. - */ -typedef struct _GstCheckLogFilter GstCheckLogFilter; - -/** - * GstCheckLogFilterFunc: - * @log_domain: the log domain of the message - * @log_level: the log level of the message - * @message: the message that has occurred - * @user_data: user data - * - * A function that is called for messages matching the filter added by - * @gst_check_add_log_filter. - * - * Returns: %TRUE if message should be discarded by GstCheck. - * - * Since: 1.12 - */ -typedef gboolean (*GstCheckLogFilterFunc) (const gchar * log_domain, - GLogLevelFlags log_level, const gchar * message, gpointer user_data); - -GST_CHECK_API -void gst_check_init (int *argc, char **argv[]); - -GST_CHECK_API -GstCheckLogFilter * gst_check_add_log_filter (const gchar * log_domain, - GLogLevelFlags log_level, GRegex * regex, GstCheckLogFilterFunc func, - gpointer user_data, GDestroyNotify destroy_data); - -GST_CHECK_API -void gst_check_remove_log_filter (GstCheckLogFilter * filter); - -GST_CHECK_API -void gst_check_clear_log_filter (void); - -GST_CHECK_API -GstFlowReturn gst_check_chain_func (GstPad * pad, GstObject * parent, GstBuffer * buffer); - -GST_CHECK_API -void gst_check_message_error (GstMessage * message, GstMessageType type, - GQuark domain, gint code); - -GST_CHECK_API -GstElement *gst_check_setup_element (const gchar * factory); - -GST_CHECK_API -void gst_check_teardown_element (GstElement * element); - -GST_CHECK_API -GstPad *gst_check_setup_src_pad (GstElement * element, - GstStaticPadTemplate * tmpl); - -GST_CHECK_API -GstPad *gst_check_setup_src_pad_from_template (GstElement * element, - GstPadTemplate * tmpl); - -GST_CHECK_API -GstPad * gst_check_setup_src_pad_by_name (GstElement * element, - GstStaticPadTemplate * tmpl, const gchar *name); - -GST_CHECK_API -GstPad * gst_check_setup_src_pad_by_name_from_template (GstElement * element, - GstPadTemplate * tmpl, const gchar *name); - -GST_CHECK_API -GstPad *gst_check_setup_sink_pad (GstElement * element, - GstStaticPadTemplate * tmpl); - -GST_CHECK_API -GstPad *gst_check_setup_sink_pad_from_template (GstElement * element, - GstPadTemplate * tmpl); - -GST_CHECK_API -GstPad * gst_check_setup_sink_pad_by_name (GstElement * element, - GstStaticPadTemplate * tmpl, const gchar *name); - -GST_CHECK_API -GstPad * gst_check_setup_sink_pad_by_name_from_template (GstElement * element, - GstPadTemplate * tmpl, const gchar *name); - -GST_CHECK_API -void gst_check_teardown_pad_by_name (GstElement * element, const gchar *name); - -GST_CHECK_API -void gst_check_teardown_src_pad (GstElement * element); - -GST_CHECK_API -void gst_check_drop_buffers (void); - -GST_CHECK_API -void gst_check_caps_equal (GstCaps * caps1, GstCaps * caps2); - -GST_CHECK_API -void gst_check_buffer_data (GstBuffer * buffer, gconstpointer data, gsize size); - -GST_CHECK_API -void gst_check_element_push_buffer_list (const gchar * element_name, - GList * buffer_in, GstCaps * caps_in, GList * buffer_out, - GstCaps * caps_out, GstFlowReturn last_flow_return); - -GST_CHECK_API -void gst_check_element_push_buffer (const gchar * element_name, - GstBuffer * buffer_in, GstCaps * caps_in, GstBuffer * buffer_out, - GstCaps *caps_out); - -GST_CHECK_API -void gst_check_teardown_sink_pad (GstElement * element); - -GST_CHECK_API -void gst_check_abi_list (GstCheckABIStruct list[], gboolean have_abi_sizes); - -GST_CHECK_API -gint gst_check_run_suite (Suite * suite, const gchar * name, - const gchar * fname); - -GST_CHECK_API -void gst_check_setup_events (GstPad * srcpad, GstElement * element, - GstCaps * caps, GstFormat format); - -GST_CHECK_API -void gst_check_setup_events_with_stream_id (GstPad * srcpad, - GstElement * element, GstCaps * caps, GstFormat format, - const gchar * stream_id); - -GST_CHECK_API -void gst_check_objects_destroyed_on_unref (gpointer object_to_unref, gpointer first_object, ...) - G_GNUC_NULL_TERMINATED; - -GST_CHECK_API -void gst_check_object_destroyed_on_unref (gpointer object_to_unref); - -#ifndef __GI_SCANNER__ - -#define fail_unless_message_error(msg, domain, code) \ -gst_check_message_error (msg, GST_MESSAGE_ERROR, \ - GST_ ## domain ## _ERROR, GST_ ## domain ## _ERROR_ ## code) -#define assert_message_error(m, d, c) fail_unless_message_error(m, d, c) - -#ifdef GST_CHECK_TEST_ENVIRONMENT_BEACON -#define GST_DO_CHECK_TEST_ENVIRONMENT \ -G_STMT_START { \ - if (g_getenv (GST_CHECK_TEST_ENVIRONMENT_BEACON) == NULL) \ - fail ("Test environment not set up correctly! Expected environment " \ - "variable '%s' to be set.", GST_CHECK_TEST_ENVIRONMENT_BEACON); \ -} G_STMT_END - -#else -#define GST_DO_CHECK_TEST_ENVIRONMENT /* nothing to check */ -#endif - -/** - * GST_START_TEST: - * @__testname: test function name - * - * wrapper for checks START_TEST - */ -/** - * GST_END_TEST: - * - * wrapper for checks END_TEST - */ -#define GST_START_TEST(__testname) \ -static void __testname (int G_GNUC_UNUSED __i__) \ -{\ - GST_DEBUG ("test start"); \ - GST_DO_CHECK_TEST_ENVIRONMENT; \ - tcase_fn_start (""# __testname, __FILE__, __LINE__); - -#define GST_END_TEST GST_LOG ("cleaning up tasks"); \ - gst_task_cleanup_all (); \ - END_TEST - -/* additional fail macros */ -/** - * fail_unless_equals_int: - * @a: a #gint value or expression - * @b: a #gint value or expression - * - * This macro checks that @a and @b are equal and aborts if this is not the - * case, printing both expressions and the values they evaluated to. This - * macro is for use in unit tests. - */ -#define fail_unless_equals_int(a, b) \ -G_STMT_START { \ - int first = a; \ - int second = b; \ - fail_unless(first == second, \ - "'" #a "' (%d) is not equal to '" #b"' (%d)", first, second); \ -} G_STMT_END; -/** - * assert_equals_int: - * @a: a #gint value or expression - * @b: a #gint value or expression - * - * This macro checks that @a and @b are equal and aborts if this is not the - * case, printing both expressions and the values they evaluated to. This - * macro is for use in unit tests. - */ -#define assert_equals_int(a, b) fail_unless_equals_int(a, b) - -/** - * fail_unless_equals_int_hex: - * @a: a #gint value or expression - * @b: a #gint value or expression - * - * This macro checks that @a and @b are equal and aborts if this is not the - * case, printing both expressions and the values they evaluated to in - * hexadecimal format. This macro is for use in unit tests. - * - * Since: 1.2 - */ -#define fail_unless_equals_int_hex(a, b) \ -G_STMT_START { \ - int first = a; \ - int second = b; \ - fail_unless(first == second, \ - "'" #a "' (0x%08x) is not equal to '" #b"' (0x%08x)", first, second);\ -} G_STMT_END; - -/** - * assert_equals_int_hex: - * @a: a #gint value or expression - * @b: a #gint value or expression - * - * This macro checks that @a and @b are equal and aborts if this is not the - * case, printing both expressions and the values they evaluated to in - * hexadecimal format. This macro is for use in unit tests. - * - * Since: 1.2 - */ -#define assert_equals_int_hex(a, b) fail_unless_equals_int_hex(a, b) - -/** - * fail_unless_equals_int64: - * @a: a #gint64 value or expression - * @b: a #gint64 value or expression - * - * This macro checks that @a and @b are equal and aborts if this is not the - * case, printing both expressions and the values they evaluated to. This - * macro is for use in unit tests. - */ -#define fail_unless_equals_int64(a, b) \ -G_STMT_START { \ - gint64 first = a; \ - gint64 second = b; \ - fail_unless(first == second, \ - "'" #a "' (%" G_GINT64_FORMAT") is not equal to '" #b"' (%" \ - G_GINT64_FORMAT")", first, second); \ -} G_STMT_END; -/** - * assert_equals_int64: - * @a: a #gint64 value or expression - * @b: a #gint64 value or expression - * - * This macro checks that @a and @b are equal and aborts if this is not the - * case, printing both expressions and the values they evaluated to. This - * macro is for use in unit tests. - */ -#define assert_equals_int64(a, b) fail_unless_equals_int64(a, b) - -/** - * fail_unless_equals_int64_hex: - * @a: a #gint64 value or expression - * @b: a #gint64 value or expression - * - * This macro checks that @a and @b are equal and aborts if this is not the - * case, printing both expressions and the values they evaluated to in - * hexadecimal format. This macro is for use in unit tests. - * - * Since: 1.2 - */ -#define fail_unless_equals_int64_hex(a, b) \ -G_STMT_START { \ - gint64 first = a; \ - gint64 second = b; \ - fail_unless(first == second, \ - "'" #a "' (0x%016x) is not equal to '" #b"' (0x%016x)", first, second);\ -} G_STMT_END; -/** - * assert_equals_int64_hex: - * @a: a #gint64 value or expression - * @b: a #gint64 value or expression - * - * This macro checks that @a and @b are equal and aborts if this is not the - * case, printing both expressions and the values they evaluated to in - * hexadecimal format. This macro is for use in unit tests. - * - * Since: 1.2 - */ -#define assert_equals_int64_hex(a,b) fail_unless_equals_int64_hex(a,b) - -/** - * fail_unless_equals_uint64: - * @a: a #guint64 value or expression - * @b: a #guint64 value or expression - * - * This macro checks that @a and @b are equal and aborts if this is not the - * case, printing both expressions and the values they evaluated to. This - * macro is for use in unit tests. - */ -#define fail_unless_equals_uint64(a, b) \ -G_STMT_START { \ - guint64 first = a; \ - guint64 second = b; \ - fail_unless(first == second, \ - "'" #a "' (%" G_GUINT64_FORMAT ") is not equal to '" #b"' (%" \ - G_GUINT64_FORMAT ")", first, second); \ -} G_STMT_END; -/** - * assert_equals_uint64: - * @a: a #guint64 value or expression - * @b: a #guint64 value or expression - * - * This macro checks that @a and @b are equal and aborts if this is not the - * case, printing both expressions and the values they evaluated to. This - * macro is for use in unit tests. - */ -#define assert_equals_uint64(a, b) fail_unless_equals_uint64(a, b) - -/** - * fail_unless_equals_uint64_hex: - * @a: a #gint64 value or expression - * @b: a #gint64 value or expression - * - * This macro checks that @a and @b are equal and aborts if this is not the - * case, printing both expressions and the values they evaluated to in - * hexadecimal format. This macro is for use in unit tests. - * - * Since: 1.2 - */ -#define fail_unless_equals_uint64_hex(a, b) \ -G_STMT_START { \ - guint64 first = a; \ - guint64 second = b; \ - fail_unless(first == second, \ - "'" #a "' (0x%016x) is not equal to '" #b"' (0x%016x)", first, second);\ -} G_STMT_END; -/** - * assert_equals_uint64_hex: - * @a: a #guint64 value or expression - * @b: a #guint64 value or expression - * - * This macro checks that @a and @b are equal and aborts if this is not the - * case, printing both expressions and the values they evaluated to in - * hexadecimal format. This macro is for use in unit tests. - * - * Since: 1.2 - */ -#define assert_equals_uint64_hex(a,b) fail_unless_equals_uint64_hex(a,b) - -/** - * fail_unless_equals_string: - * @a: a string literal or expression - * @b: a string literal or expression - * - * This macro checks that @a and @b are equal (as per g_strcmp0()) and aborts if - * this is not the case, printing both expressions and the values they - * evaluated to. This macro is for use in unit tests. - */ -#define fail_unless_equals_string(a, b) \ -G_STMT_START { \ - const gchar * first = a; \ - const gchar * second = b; \ - fail_unless(g_strcmp0 (first, second) == 0, \ - "'" #a "' (%s) is not equal to '" #b"' (%s)", first, second); \ -} G_STMT_END; -/** - * assert_equals_string: - * @a: a string literal or expression - * @b: a string literal or expression - * - * This macro checks that @a and @b are equal (as per g_strcmp0()) and aborts if - * this is not the case, printing both expressions and the values they - * evaluated to. This macro is for use in unit tests. - */ -#define assert_equals_string(a, b) fail_unless_equals_string(a, b) - -/** - * fail_unless_equals_float: - * @a: a #gdouble or #gfloat value or expression - * @b: a #gdouble or #gfloat value or expression - * - * This macro checks that @a and @b are (almost) equal and aborts if this - * is not the case, printing both expressions and the values they evaluated - * to. This macro is for use in unit tests. - */ -#define fail_unless_equals_float(a, b) \ -G_STMT_START { \ - double first = a; \ - double second = b; \ - /* This will only work for 'normal' values and values around 0, \ - * which should be good enough for our purposes here */ \ - fail_unless(fabs (first - second) < 0.0000001, \ - "'" #a "' (%g) is not equal to '" #b "' (%g)", first, second);\ -} G_STMT_END; - -/** - * assert_equals_float: - * @a: a #gdouble or #gfloat value or expression - * @b: a #gdouble or #gfloat value or expression - * - * This macro checks that @a and @b are (almost) equal and aborts if this - * is not the case, printing both expressions and the values they evaluated - * to. This macro is for use in unit tests. - */ -#define assert_equals_float(a, b) fail_unless_equals_float(a, b) - -/** - * fail_unless_equals_pointer: - * @a: a pointer value or expression - * @b: a pointer value or expression - * - * This macro checks that @a and @b are equal and aborts if this - * is not the case, printing both expressions and the values they - * evaluated to. This macro is for use in unit tests. - * - * Since: 1.2 - */ -#define fail_unless_equals_pointer(a, b) \ -G_STMT_START { \ - gpointer first = a; \ - gpointer second = b; \ - fail_unless(first == second, \ - "'" #a "' (%p) is not equal to '" #b "' (%p)", first, second);\ -} G_STMT_END; - -/** - * assert_equals_pointer: - * @a: a pointer value or expression - * @b: a pointer value or expression - * - * This macro checks that @a and @b are equal and aborts if this - * is not the case, printing both expressions and the values they - * evaluated to. This macro is for use in unit tests. - * - * Since: 1.2 - */ -#define assert_equals_pointer(a, b) fail_unless_equals_pointer(a, b) - -/** - * fail_unless_equals_clocktime: - * @a: a #GstClockTime value or expression - * @b: a #GstClockTime value or expression - * - * This macro checks that @a and @b are equal and aborts if this is not the - * case, printing both expressions and the values they evaluated to. This - * macro is for use in unit tests. - */ -#define fail_unless_equals_clocktime(a, b) \ -G_STMT_START { \ - GstClockTime first = a; \ - GstClockTime second = b; \ - fail_unless(first == second, \ - "'" #a "' (%" GST_TIME_FORMAT") is not equal to '" #b"' (%" GST_TIME_FORMAT")", \ - GST_TIME_ARGS (first), GST_TIME_ARGS (second)); \ -} G_STMT_END; - -/*** - * thread test macros and variables - */ -GST_CHECK_API GList *thread_list; -GST_CHECK_API GMutex mutex; -GST_CHECK_API GCond start_cond; /* used to notify main thread of thread startups */ -GST_CHECK_API GCond sync_cond; /* used to synchronize all threads and main thread */ - -#define MAIN_START_THREADS(count, function, data) \ -MAIN_INIT(); \ -MAIN_START_THREAD_FUNCTIONS(count, function, data); \ -MAIN_SYNCHRONIZE(); - -#define MAIN_INIT() \ -G_STMT_START { \ - g_mutex_init (&mutex); \ - g_cond_init (&start_cond); \ - g_cond_init (&sync_cond); \ - _gst_check_threads_running = TRUE; \ -} G_STMT_END; - -#define MAIN_START_THREAD_FUNCTIONS(count, function, data) \ -G_STMT_START { \ - int i; \ - for (i = 0; i < count; ++i) { \ - MAIN_START_THREAD_FUNCTION (i, function, data); \ - } \ -} G_STMT_END; - -#define MAIN_START_THREAD_FUNCTION(i, function, data) \ -G_STMT_START { \ - GThread *thread = NULL; \ - GST_DEBUG ("MAIN: creating thread %d", i); \ - g_mutex_lock (&mutex); \ - thread = g_thread_try_new ("gst-check", \ - (GThreadFunc) function, data, NULL); \ - /* wait for thread to signal us that it's ready */ \ - GST_DEBUG ("MAIN: waiting for thread %d", i); \ - g_cond_wait (&start_cond, &mutex); \ - g_mutex_unlock (&mutex); \ - \ - thread_list = g_list_append (thread_list, thread); \ -} G_STMT_END; - - -#define MAIN_SYNCHRONIZE() \ -G_STMT_START { \ - GST_DEBUG ("MAIN: synchronizing"); \ - g_cond_broadcast (&sync_cond); \ - GST_DEBUG ("MAIN: synchronized"); \ -} G_STMT_END; - -#define MAIN_STOP_THREADS() \ -G_STMT_START { \ - _gst_check_threads_running = FALSE; \ - \ - /* join all threads */ \ - GST_DEBUG ("MAIN: joining"); \ - g_list_foreach (thread_list, (GFunc) g_thread_join, NULL); \ - g_list_free (thread_list); \ - thread_list = NULL; \ - g_mutex_clear (&mutex); \ - g_cond_clear (&start_cond); \ - g_cond_clear (&sync_cond); \ - GST_DEBUG ("MAIN: joined"); \ -} G_STMT_END; - -#define THREAD_START() \ -THREAD_STARTED(); \ -THREAD_SYNCHRONIZE(); - -#define THREAD_STARTED() \ -G_STMT_START { \ - /* signal main thread that we started */ \ - GST_DEBUG ("THREAD %p: started", g_thread_self ()); \ - g_mutex_lock (&mutex); \ - g_cond_signal (&start_cond); \ -} G_STMT_END; - -#define THREAD_SYNCHRONIZE() \ -G_STMT_START { \ - /* synchronize everyone */ \ - GST_DEBUG ("THREAD %p: syncing", g_thread_self ()); \ - fail_if (g_mutex_trylock (&mutex), \ - "bug in unit test, mutex should be locked at this point");\ - g_cond_wait (&sync_cond, &mutex); \ - GST_DEBUG ("THREAD %p: synced", g_thread_self ()); \ - g_mutex_unlock (&mutex); \ -} G_STMT_END; - -#define THREAD_SWITCH() \ -G_STMT_START { \ - g_thread_yield (); \ -} G_STMT_END; - -#define THREAD_TEST_RUNNING() (!!_gst_check_threads_running) - -/* additional assertions */ - -#if GST_DISABLE_GLIB_CHECKS -#define ASSERT_CRITICAL(code) -#else -#define ASSERT_CRITICAL(code) \ -G_STMT_START { \ - _gst_check_expecting_log = TRUE; \ - _gst_check_raised_critical = FALSE; \ - code; \ - if (!_gst_check_raised_critical) \ - _ck_assert_failed (__FILE__, __LINE__, \ - "Expected g_critical, got nothing", NULL); \ - _gst_check_expecting_log = FALSE; \ -} G_STMT_END -#endif /* GST_DISABLE_GLIB_CHECKS */ - -#define ASSERT_WARNING(code) \ -G_STMT_START { \ - _gst_check_expecting_log = TRUE; \ - _gst_check_raised_warning = FALSE; \ - code; \ - if (!_gst_check_raised_warning) \ - _ck_assert_failed (__FILE__, __LINE__, \ - "Expected g_warning, got nothing", NULL); \ - _gst_check_expecting_log = FALSE; \ -} G_STMT_END - - -#define ASSERT_OBJECT_REFCOUNT(object, name, value) \ -G_STMT_START { \ - int rc; \ - rc = GST_OBJECT_REFCOUNT_VALUE (object); \ - fail_unless (rc == value, \ - "%s (%p) refcount is %d instead of %d", \ - name, object, rc, value); \ -} G_STMT_END - -#define ASSERT_OBJECT_REFCOUNT_BETWEEN(object, name, lower, upper) \ -G_STMT_START { \ - int rc = GST_OBJECT_REFCOUNT_VALUE (object); \ - int lo = lower; \ - int hi = upper; \ - \ - fail_unless (rc >= lo, \ - "%s (%p) refcount %d is smaller than %d", \ - name, object, rc, lo); \ - fail_unless (rc <= hi, \ - "%s (%p) refcount %d is bigger than %d", \ - name, object, rc, hi); \ -} G_STMT_END - - -#define ASSERT_CAPS_REFCOUNT(caps, name, value) \ - ASSERT_MINI_OBJECT_REFCOUNT(caps, name, value) - -#define ASSERT_BUFFER_REFCOUNT(buffer, name, value) \ - ASSERT_MINI_OBJECT_REFCOUNT(buffer, name, value) - -#define ASSERT_MINI_OBJECT_REFCOUNT(miniobj, name, value) \ -G_STMT_START { \ - int rc; \ - rc = GST_MINI_OBJECT_REFCOUNT_VALUE (miniobj); \ - fail_unless (rc == value, \ - name " (%p) refcount is %d instead of %d", miniobj, rc, value); \ -} G_STMT_END - -#define ASSERT_SET_STATE(element, state, ret) \ -fail_unless (gst_element_set_state (GST_ELEMENT(element), \ - state) == ret, \ - "could not change state to " #state); - -#define GST_CHECK_MAIN(name) \ -int main (int argc, char **argv) \ -{ \ - Suite *s; \ - gst_check_init (&argc, &argv); \ - s = name ## _suite (); \ - return gst_check_run_suite (s, # name, __FILE__); \ -} - -/* Hack to allow run-time selection of unit tests to run via the - * GST_CHECKS environment variable (test function names globs, comma - * separated), or GST_CHECKS_IGNORE with the same semantics */ - -GST_CHECK_API -gboolean _gst_check_run_test_func (const gchar * func_name); - -static inline void -__gst_tcase_add_test (TCase * tc, TFun tf, const char * fname, int signal, - int allowed_exit_value, int start, int end) -{ - if (_gst_check_list_tests) { - g_print ("Test: %s\n", fname); - return; - } - - if (_gst_check_run_test_func (fname)) { - _tcase_add_test (tc, tf, fname, signal, allowed_exit_value, start, end); - } -} - -#define _tcase_add_test __gst_tcase_add_test - -/* A special variant to add broken tests. These are normally skipped, but can be - * forced to run via GST_CHECKS */ -#define tcase_skip_broken_test(chain,test_func) \ -G_STMT_START { \ - const char *env = g_getenv ("GST_CHECKS"); \ - \ - if (env != NULL && g_pattern_match_simple (env, G_STRINGIFY (test_func))) { \ - tcase_add_test(chain,test_func); \ - } else { \ - g_printerr ("FIXME: skipping test %s because it's broken\n", G_STRINGIFY (test_func)); \ - } \ -} G_STMT_END - -#define tcase_skip_broken_loop_test(chain,test_func,a,b) \ - tcase_skip_broken_test (chain, test_func) - -#endif /* !__GI_SCANNER__ */ - -G_END_DECLS - -#endif /* __GST_CHECK_H__ */ diff --git a/libs/gst/check/gstconsistencychecker.c b/libs/gst/check/gstconsistencychecker.c deleted file mode 100644 index 08f720e89c..0000000000 --- a/libs/gst/check/gstconsistencychecker.c +++ /dev/null @@ -1,305 +0,0 @@ -/* GStreamer - * - * unit testing helper lib - * - * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com> - * Copyright (C) 2012 Stefan Sauer <ensonic@users.sf.net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * SECTION:gstcheckconsistencychecker - * @title: GstStreamConsistencyChecker - * @short_description: Data flow consistency checker for GStreamer unit tests. - * - * These macros and functions are for internal use of the unit tests found - * inside the 'check' directories of various GStreamer packages. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstconsistencychecker.h" - -struct _GstStreamConsistency -{ - /* FIXME: do we want to track some states per pad? */ - gboolean flushing; - gboolean segment; - gboolean eos; - gboolean expect_flush; - gboolean saw_serialized_event; - gboolean saw_stream_start; - GstObject *parent; - GList *pads; -}; - -typedef struct _GstStreamConsistencyProbe -{ - GstPad *pad; - gulong probeid; -} GstStreamConsistencyProbe; - -static gboolean -source_pad_data_cb (GstPad * pad, GstPadProbeInfo * info, - GstStreamConsistency * consist) -{ - GstMiniObject *data = GST_PAD_PROBE_INFO_DATA (info); - - GST_DEBUG_OBJECT (pad, "%p: %d %d %d %d", consist, consist->flushing, - consist->segment, consist->eos, consist->expect_flush); - - if (GST_IS_BUFFER (data)) { - GST_DEBUG_OBJECT (pad, - "Buffer pts %" GST_TIME_FORMAT ", dts %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_PTS (GST_BUFFER_CAST (data))), - GST_TIME_ARGS (GST_BUFFER_DTS (GST_BUFFER_CAST (data)))); - /* If an EOS went through, a buffer would be invalid */ - fail_if (consist->eos, "Buffer received after EOS on pad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - /* Buffers need to be preceded by a segment event */ - fail_unless (consist->segment, "Buffer received without segment " - "on pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - } else if (GST_IS_EVENT (data)) { - GstEvent *event = (GstEvent *) data; - - GST_DEBUG_OBJECT (pad, "Event : %s", GST_EVENT_TYPE_NAME (event)); - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_START: - /* getting two flush_start in a row seems to be okay - fail_if (consist->flushing, "Received another FLUSH_START"); - */ - consist->flushing = TRUE; - break; - case GST_EVENT_FLUSH_STOP: - /* Receiving a flush-stop is only valid after receiving a flush-start */ - fail_unless (consist->flushing, - "Received a FLUSH_STOP without a FLUSH_START on pad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - fail_if (consist->eos, "Received a FLUSH_STOP after an EOS on " - "pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - consist->flushing = consist->expect_flush = FALSE; - break; - case GST_EVENT_STREAM_START: - fail_if (consist->saw_serialized_event && !consist->saw_stream_start, - "Got a STREAM_START event after a serialized event on pad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - consist->saw_stream_start = TRUE; - break; - case GST_EVENT_CAPS: - /* ok to have these before segment event */ - /* FIXME check order more precisely, if so spec'ed somehow ? */ - break; - case GST_EVENT_SEGMENT: - fail_if ((consist->expect_flush && consist->flushing), - "Received SEGMENT while in a flushing seek on pad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - consist->segment = TRUE; - consist->eos = FALSE; - break; - case GST_EVENT_EOS: - /* FIXME : not 100% sure about whether two eos in a row is valid */ - fail_if (consist->eos, "Received EOS just after another EOS on " - "pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - consist->eos = TRUE; - consist->segment = FALSE; - break; - case GST_EVENT_TAG: - GST_DEBUG_OBJECT (pad, "tag %" GST_PTR_FORMAT, - gst_event_get_structure (event)); - /* fall through */ - default: - if (GST_EVENT_IS_SERIALIZED (event) && GST_EVENT_IS_DOWNSTREAM (event)) { - fail_if (consist->eos, "Event received after EOS"); - fail_unless (consist->segment, "Event %s received before segment " - "on pad %s:%s", GST_EVENT_TYPE_NAME (event), - GST_DEBUG_PAD_NAME (pad)); - } - /* FIXME : Figure out what to do for other events */ - break; - } - if (GST_EVENT_IS_SERIALIZED (event)) { - fail_if (!consist->saw_stream_start - && GST_EVENT_TYPE (event) != GST_EVENT_STREAM_START, - "Got a serialized event (%s) before a STREAM_START on pad %s:%s", - GST_EVENT_TYPE_NAME (event), GST_DEBUG_PAD_NAME (pad)); - consist->saw_serialized_event = TRUE; - } - } - - return TRUE; -} - -static gboolean -sink_pad_data_cb (GstPad * pad, GstPadProbeInfo * info, - GstStreamConsistency * consist) -{ - GstMiniObject *data = GST_PAD_PROBE_INFO_DATA (info); - - GST_DEBUG_OBJECT (pad, "%p: %d %d %d %d", consist, consist->flushing, - consist->segment, consist->eos, consist->expect_flush); - - if (GST_IS_BUFFER (data)) { - GST_DEBUG_OBJECT (pad, "Buffer %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (GST_BUFFER (data)))); - /* If an EOS went through, a buffer would be invalid */ - fail_if (consist->eos, "Buffer received after EOS on pad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - /* Buffers need to be preceded by a segment event */ - fail_unless (consist->segment, "Buffer received without segment " - "on pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - } else if (GST_IS_EVENT (data)) { - GstEvent *event = (GstEvent *) data; - - GST_DEBUG_OBJECT (pad, "%s", GST_EVENT_TYPE_NAME (event)); - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_SEEK: - { - GstSeekFlags flags; - - gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, - NULL); - consist->expect_flush = - ((flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH); - break; - } - case GST_EVENT_SEGMENT: - fail_if ((consist->expect_flush && consist->flushing), - "Received SEGMENT while in a flushing seek on pad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - consist->segment = TRUE; - consist->eos = FALSE; - break; - default: - /* FIXME : Figure out what to do for other events */ - break; - } - } - - return TRUE; -} - -static void -add_pad (GstStreamConsistency * consist, GstPad * pad) -{ - GstStreamConsistencyProbe *p; - GstPadDirection dir; - - p = g_new0 (GstStreamConsistencyProbe, 1); - p->pad = g_object_ref (pad); - dir = gst_pad_get_direction (pad); - if (dir == GST_PAD_SRC) { - - p->probeid = - gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM, - (GstPadProbeCallback) source_pad_data_cb, consist, NULL); - - } else if (dir == GST_PAD_SINK) { - p->probeid = - gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM, - (GstPadProbeCallback) sink_pad_data_cb, consist, NULL); - } - consist->pads = g_list_prepend (consist->pads, p); -} - -/** - * gst_consistency_checker_new: (skip) - * @pad: The #GstPad on which the dataflow will be checked. - * - * Sets up a data probe on the given pad which will raise assertions if the - * data flow is inconsistent. - * - * Returns: A #GstStreamConsistency structure used to track data flow. - */ -GstStreamConsistency * -gst_consistency_checker_new (GstPad * pad) -{ - GstStreamConsistency *consist; - - g_return_val_if_fail (pad != NULL, NULL); - - consist = g_new0 (GstStreamConsistency, 1); - - if (!consist->pads) { - consist->parent = GST_OBJECT_PARENT (pad); - } - add_pad (consist, pad); - return consist; -} - -/** - * gst_consistency_checker_add_pad: - * @consist: The #GstStreamConsistency handle - * @pad: The #GstPad on which the dataflow will be checked. - * - * Sets up a data probe on the given pad which will raise assertions if the - * data flow is inconsistent. - * - * Returns: %TRUE if the pad was added - */ -gboolean -gst_consistency_checker_add_pad (GstStreamConsistency * consist, GstPad * pad) -{ - g_return_val_if_fail (consist != NULL, FALSE); - g_return_val_if_fail (pad != NULL, FALSE); - g_return_val_if_fail (GST_OBJECT_PARENT (pad) == consist->parent, FALSE); - - add_pad (consist, pad); - return TRUE; -} - -/** - * gst_consistency_checker_reset: - * @consist: The #GstStreamConsistency to reset. - * - * Reset the stream checker's internal variables. - */ - -void -gst_consistency_checker_reset (GstStreamConsistency * consist) -{ - consist->flushing = FALSE; - consist->segment = FALSE; - consist->eos = FALSE; - consist->expect_flush = FALSE; - consist->saw_serialized_event = FALSE; - consist->saw_stream_start = FALSE; -} - -/** - * gst_consistency_checker_free: - * @consist: The #GstStreamConsistency to free. - * - * Frees the allocated data and probes associated with @consist. - */ - -void -gst_consistency_checker_free (GstStreamConsistency * consist) -{ - GList *node; - GstStreamConsistencyProbe *p; - - /* Remove the data probes */ - for (node = consist->pads; node; node = g_list_next (node)) { - p = (GstStreamConsistencyProbe *) node->data; - gst_pad_remove_probe (p->pad, p->probeid); - gst_object_unref (p->pad); - g_free (p); - } - g_list_free (consist->pads); - g_free (consist); -} diff --git a/libs/gst/check/gstconsistencychecker.h b/libs/gst/check/gstconsistencychecker.h deleted file mode 100644 index 03ce583a8f..0000000000 --- a/libs/gst/check/gstconsistencychecker.h +++ /dev/null @@ -1,52 +0,0 @@ -/* GStreamer - * - * unit testing helper lib - * - * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_CONSISTENCY_CHECKER_H__ -#define __GST_CONSISTENCY_CHECKER_H__ - -#include <gst/check/gstcheck.h> - -G_BEGIN_DECLS - -/** - * GstStreamConsistency: - * - * Opaque consistency checker handle. - */ -typedef struct _GstStreamConsistency GstStreamConsistency; - -GST_CHECK_API -GstStreamConsistency * gst_consistency_checker_new (GstPad * pad); - -GST_CHECK_API -gboolean gst_consistency_checker_add_pad (GstStreamConsistency * consist, - GstPad * pad); - -GST_CHECK_API -void gst_consistency_checker_reset (GstStreamConsistency * consist); - -GST_CHECK_API -void gst_consistency_checker_free (GstStreamConsistency * consist); - -G_END_DECLS - -#endif /* __GST_CONSISTENCY_CHECKER_H__ */ diff --git a/libs/gst/check/gstharness.c b/libs/gst/check/gstharness.c deleted file mode 100644 index fc1ab0dafe..0000000000 --- a/libs/gst/check/gstharness.c +++ /dev/null @@ -1,3517 +0,0 @@ -/* GstHarness - A test-harness for GStreamer testing - * - * Copyright (C) 2012-2015 Pexip <pexip.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/** - * SECTION:gstharness - * @title: GstHarness - * @short_description: A test-harness for writing GStreamer unit tests - * @see_also: #GstTestClock - * - * #GstHarness is meant to make writing unit test for GStreamer much easier. - * It can be thought of as a way of treating a #GstElement as a black box, - * deterministically feeding it data, and controlling what data it outputs. - * - * The basic structure of #GstHarness is two "floating" #GstPads that connect - * to the harnessed #GstElement src and sink #GstPads like so: - * - * |[ - * __________________________ - * _____ | _____ _____ | _____ - * | | | | | | | | | | - * | src |--+-| sink| Element | src |-+--| sink| - * |_____| | |_____| |_____| | |_____| - * |__________________________| - * - * ]| - * - * With this, you can now simulate any environment the #GstElement might find - * itself in. By specifying the #GstCaps of the harness #GstPads, using - * functions like gst_harness_set_src_caps() or gst_harness_set_sink_caps_str(), - * you can test how the #GstElement interacts with different caps sets. - * - * Your harnessed #GstElement can of course also be a bin, and using - * gst_harness_new_parse() supporting standard gst-launch syntax, you can - * easily test a whole pipeline instead of just one element. - * - * You can then go on to push #GstBuffers and #GstEvents on to the srcpad, - * using functions like gst_harness_push() and gst_harness_push_event(), and - * then pull them out to examine them with gst_harness_pull() and - * gst_harness_pull_event(). - * - * ## A simple buffer-in buffer-out example - * - * |[<!-- language="C" --> - * #include <gst/gst.h> - * #include <gst/check/gstharness.h> - * GstHarness *h; - * GstBuffer *in_buf; - * GstBuffer *out_buf; - * - * // attach the harness to the src and sink pad of GstQueue - * h = gst_harness_new ("queue"); - * - * // we must specify a caps before pushing buffers - * gst_harness_set_src_caps_str (h, "mycaps"); - * - * // create a buffer of size 42 - * in_buf = gst_harness_create_buffer (h, 42); - * - * // push the buffer into the queue - * gst_harness_push (h, in_buf); - * - * // pull the buffer from the queue - * out_buf = gst_harness_pull (h); - * - * // validate the buffer in is the same as buffer out - * fail_unless (in_buf == out_buf); - * - * // cleanup - * gst_buffer_unref (out_buf); - * gst_harness_teardown (h); - * - * ]| - * - * Another main feature of the #GstHarness is its integration with the - * #GstTestClock. Operating the #GstTestClock can be very challenging, but - * #GstHarness simplifies some of the most desired actions a lot, like wanting - * to manually advance the clock while at the same time releasing a #GstClockID - * that is waiting, with functions like gst_harness_crank_single_clock_wait(). - * - * #GstHarness also supports sub-harnesses, as a way of generating and - * validating data. A sub-harness is another #GstHarness that is managed by - * the "parent" harness, and can either be created by using the standard - * gst_harness_new type functions directly on the (GstHarness *)->src_harness, - * or using the much more convenient gst_harness_add_src() or - * gst_harness_add_sink_parse(). If you have a decoder-element you want to test, - * (like vp8dec) it can be very useful to add a src-harness with both a - * src-element (videotestsrc) and an encoder (vp8enc) to feed the decoder data - * with different configurations, by simply doing: - * - * |[<!-- language="C" --> - * GstHarness * h = gst_harness_new (h, "vp8dec"); - * gst_harness_add_src_parse (h, "videotestsrc is-live=1 ! vp8enc", TRUE); - * ]| - * - * and then feeding it data with: - * - * |[<!-- language="C" --> - * gst_harness_push_from_src (h); - * ]| - * - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -/* we have code with side effects in asserts, so make sure they are active */ -#ifdef G_DISABLE_ASSERT -#error "GstHarness must be compiled with G_DISABLE_ASSERT undefined" -#endif - -#include "gstharness.h" - -#include <stdio.h> -#include <string.h> -#include <math.h> - -static void gst_harness_stress_free (GstHarnessThread * t); - -#define HARNESS_KEY "harness" -#define HARNESS_REF "harness-ref" -#define HARNESS_LOCK(h) g_mutex_lock (&(h)->priv->priv_mutex) -#define HARNESS_UNLOCK(h) g_mutex_unlock (&(h)->priv->priv_mutex) - -static GstStaticPadTemplate hsrctemplate = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); -static GstStaticPadTemplate hsinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); - -typedef struct -{ - GType api; - GstStructure *params; -} ProposeMeta; - -static void -propose_meta_clear (ProposeMeta * meta) -{ - if (meta->params) - gst_structure_free (meta->params); -} - -struct _GstHarnessPrivate -{ - gchar *element_sinkpad_name; - gchar *element_srcpad_name; - - GstCaps *src_caps; - GstCaps *sink_caps; - - gboolean forwarding; - GstPad *sink_forward_pad; - GstTestClock *testclock; - - gint recv_buffers; - gint recv_events; - gint recv_upstream_events; - - GAsyncQueue *buffer_queue; - GAsyncQueue *src_event_queue; - GAsyncQueue *sink_event_queue; - - GstClockTime latency_min; - GstClockTime latency_max; - gboolean is_live; - - gboolean has_clock_wait; - gboolean drop_buffers; - GstClockTime last_push_ts; - - GstBufferPool *pool; - GstAllocator *allocator; - GstAllocationParams allocation_params; - GstAllocator *propose_allocator; - GstAllocationParams propose_allocation_params; - - GArray *propose_allocation_metas; - - gboolean blocking_push_mode; - GCond blocking_push_cond; - GMutex blocking_push_mutex; - GMutex priv_mutex; - - GCond buf_or_eos_cond; - GMutex buf_or_eos_mutex; - gboolean eos_received; - - GPtrArray *stress; -}; - -static GstFlowReturn -gst_harness_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) -{ - GstHarness *h = g_object_get_data (G_OBJECT (pad), HARNESS_KEY); - GstHarnessPrivate *priv = h->priv; - (void) parent; - g_assert (h != NULL); - g_mutex_lock (&priv->blocking_push_mutex); - g_atomic_int_inc (&priv->recv_buffers); - - if (priv->drop_buffers) { - gst_buffer_unref (buffer); - } else { - g_mutex_lock (&priv->buf_or_eos_mutex); - g_async_queue_push (priv->buffer_queue, buffer); - g_cond_signal (&priv->buf_or_eos_cond); - g_mutex_unlock (&priv->buf_or_eos_mutex); - } - - - if (priv->blocking_push_mode) { - g_cond_wait (&priv->blocking_push_cond, &priv->blocking_push_mutex); - } - g_mutex_unlock (&priv->blocking_push_mutex); - - return GST_FLOW_OK; -} - -static gboolean -gst_harness_src_event (GstPad * pad, GstObject * parent, GstEvent * event) -{ - GstHarness *h = g_object_get_data (G_OBJECT (pad), HARNESS_KEY); - GstHarnessPrivate *priv = h->priv; - (void) parent; - g_assert (h != NULL); - g_atomic_int_inc (&priv->recv_upstream_events); - g_async_queue_push (priv->src_event_queue, event); - return TRUE; -} - -static gboolean -gst_harness_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) -{ - GstHarness *h = g_object_get_data (G_OBJECT (pad), HARNESS_KEY); - GstHarnessPrivate *priv = h->priv; - gboolean ret = TRUE; - gboolean forward; - - g_assert (h != NULL); - (void) parent; - g_atomic_int_inc (&priv->recv_events); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_STREAM_START: - case GST_EVENT_CAPS: - case GST_EVENT_SEGMENT: - forward = TRUE; - break; - default: - forward = FALSE; - break; - } - - HARNESS_LOCK (h); - if (priv->forwarding && forward && priv->sink_forward_pad) { - GstPad *fwdpad = gst_object_ref (priv->sink_forward_pad); - HARNESS_UNLOCK (h); - ret = gst_pad_push_event (fwdpad, event); - gst_object_unref (fwdpad); - HARNESS_LOCK (h); - } else { - if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { - g_mutex_lock (&priv->buf_or_eos_mutex); - priv->eos_received = TRUE; - g_cond_signal (&priv->buf_or_eos_cond); - g_mutex_unlock (&priv->buf_or_eos_mutex); - } - g_async_queue_push (priv->sink_event_queue, event); - } - HARNESS_UNLOCK (h); - - return ret; -} - -static void -gst_harness_decide_allocation (GstHarness * h, GstCaps * caps) -{ - GstHarnessPrivate *priv = h->priv; - GstQuery *query; - GstAllocator *allocator; - GstAllocationParams params; - GstBufferPool *pool = NULL; - guint size, min, max; - - query = gst_query_new_allocation (caps, FALSE); - gst_pad_peer_query (h->srcpad, query); - - if (gst_query_get_n_allocation_params (query) > 0) { - gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶ms); - } else { - allocator = NULL; - gst_allocation_params_init (¶ms); - } - - if (gst_query_get_n_allocation_pools (query) > 0) { - gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); -#if 0 - /* Most elements create their own pools if pool == NULL. Not sure if we - * want to do that in the harness since we may want to test the pool - * implementation of the elements. Not creating a pool will however ignore - * the returned size. */ - if (pool == NULL) - pool = gst_buffer_pool_new (); -#endif - } else { - pool = NULL; - size = min = max = 0; - } - gst_query_unref (query); - - if (pool) { - GstStructure *config = gst_buffer_pool_get_config (pool); - gst_buffer_pool_config_set_params (config, caps, size, min, max); - gst_buffer_pool_config_set_allocator (config, allocator, ¶ms); - gst_buffer_pool_set_config (pool, config); - } - - if (pool != priv->pool) { - if (priv->pool != NULL) - gst_buffer_pool_set_active (priv->pool, FALSE); - if (pool) - gst_buffer_pool_set_active (pool, TRUE); - } - - priv->allocation_params = params; - if (priv->allocator) - gst_object_unref (priv->allocator); - priv->allocator = allocator; - if (priv->pool) - gst_object_unref (priv->pool); - priv->pool = pool; -} - -static void -gst_harness_negotiate (GstHarness * h) -{ - GstCaps *caps; - - caps = gst_pad_get_current_caps (h->srcpad); - if (caps != NULL) { - gst_harness_decide_allocation (h, caps); - gst_caps_unref (caps); - } else { - GST_FIXME_OBJECT (h->srcpad, - "Cannot negotiate allocation because caps is not set"); - } -} - -static gboolean -gst_harness_sink_query (GstPad * pad, GstObject * parent, GstQuery * query) -{ - GstHarness *h = g_object_get_data (G_OBJECT (pad), HARNESS_KEY); - GstHarnessPrivate *priv = h->priv; - gboolean res = TRUE; - g_assert (h != NULL); - - // FIXME: forward all queries? - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_LATENCY: - gst_query_set_latency (query, priv->is_live, priv->latency_min, - priv->latency_max); - break; - case GST_QUERY_CAPS: - { - GstCaps *caps, *filter = NULL; - - if (priv->sink_caps) { - caps = gst_caps_ref (priv->sink_caps); - } else { - caps = gst_pad_get_pad_template_caps (pad); - } - - gst_query_parse_caps (query, &filter); - if (filter != NULL) { - gst_caps_take (&caps, - gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST)); - } - - gst_query_set_caps_result (query, caps); - gst_caps_unref (caps); - } - break; - case GST_QUERY_ALLOCATION: - { - HARNESS_LOCK (h); - if (priv->forwarding && priv->sink_forward_pad != NULL) { - GstPad *peer = gst_pad_get_peer (priv->sink_forward_pad); - g_assert (peer != NULL); - HARNESS_UNLOCK (h); - res = gst_pad_query (peer, query); - gst_object_unref (peer); - HARNESS_LOCK (h); - } else { - GstCaps *caps; - gboolean need_pool; - guint size; - - gst_query_parse_allocation (query, &caps, &need_pool); - - /* FIXME: Can this be removed? */ - size = gst_query_get_n_allocation_params (query); - g_assert_cmpuint (0, ==, size); - gst_query_add_allocation_param (query, - priv->propose_allocator, &priv->propose_allocation_params); - - if (priv->propose_allocation_metas) { - guint i; - for (i = 0; i < priv->propose_allocation_metas->len; i++) { - ProposeMeta *meta = - &g_array_index (priv->propose_allocation_metas, ProposeMeta, i); - gst_query_add_allocation_meta (query, meta->api, meta->params); - } - } - - GST_DEBUG_OBJECT (pad, "proposing allocation %" GST_PTR_FORMAT, - priv->propose_allocator); - } - HARNESS_UNLOCK (h); - break; - } - case GST_QUERY_CONTEXT: - HARNESS_LOCK (h); - if (priv->forwarding && priv->sink_forward_pad != NULL) { - GstPad *peer = gst_pad_get_peer (priv->sink_forward_pad); - g_assert (peer != NULL); - HARNESS_UNLOCK (h); - res = gst_pad_query (peer, query); - gst_object_unref (peer); - } else { - HARNESS_UNLOCK (h); - res = gst_pad_query_default (pad, parent, query); - } - break; - default: - res = gst_pad_query_default (pad, parent, query); - } - - return res; -} - -static gboolean -gst_harness_src_query (GstPad * pad, GstObject * parent, GstQuery * query) -{ - GstHarness *h = g_object_get_data (G_OBJECT (pad), HARNESS_KEY); - GstHarnessPrivate *priv = h->priv; - gboolean res = TRUE; - g_assert (h != NULL); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_LATENCY: - gst_query_set_latency (query, priv->is_live, priv->latency_min, - priv->latency_max); - break; - case GST_QUERY_CAPS: - { - GstCaps *caps, *filter = NULL; - - if (priv->src_caps) { - caps = gst_caps_ref (priv->src_caps); - } else { - caps = gst_pad_get_pad_template_caps (pad); - } - - gst_query_parse_caps (query, &filter); - if (filter != NULL) { - gst_caps_take (&caps, - gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST)); - } - - gst_query_set_caps_result (query, caps); - gst_caps_unref (caps); - } - break; - default: - res = gst_pad_query_default (pad, parent, query); - } - return res; -} - -static void -gst_harness_element_ref (GstHarness * h) -{ - guint *data; - - GST_OBJECT_LOCK (h->element); - data = g_object_get_data (G_OBJECT (h->element), HARNESS_REF); - if (data == NULL) { - data = g_new0 (guint, 1); - *data = 1; - g_object_set_data_full (G_OBJECT (h->element), HARNESS_REF, data, g_free); - } else { - (*data)++; - } - GST_OBJECT_UNLOCK (h->element); -} - -static guint -gst_harness_element_unref (GstHarness * h) -{ - guint *data; - guint ret; - - GST_OBJECT_LOCK (h->element); - data = g_object_get_data (G_OBJECT (h->element), HARNESS_REF); - g_assert (data != NULL); - (*data)--; - ret = *data; - GST_OBJECT_UNLOCK (h->element); - - return ret; -} - -static void -gst_harness_link_element_srcpad (GstHarness * h, - const gchar * element_srcpad_name) -{ - GstHarnessPrivate *priv = h->priv; - GstPad *srcpad = gst_element_get_static_pad (h->element, - element_srcpad_name); - GstPadLinkReturn link; - if (srcpad == NULL) - srcpad = gst_element_request_pad_simple (h->element, element_srcpad_name); - g_assert (srcpad); - link = gst_pad_link (srcpad, h->sinkpad); - g_assert_cmpint (link, ==, GST_PAD_LINK_OK); - g_free (priv->element_srcpad_name); - priv->element_srcpad_name = gst_pad_get_name (srcpad); - - gst_object_unref (srcpad); -} - -static void -gst_harness_link_element_sinkpad (GstHarness * h, - const gchar * element_sinkpad_name) -{ - GstHarnessPrivate *priv = h->priv; - GstPad *sinkpad = gst_element_get_static_pad (h->element, - element_sinkpad_name); - GstPadLinkReturn link; - if (sinkpad == NULL) - sinkpad = gst_element_request_pad_simple (h->element, element_sinkpad_name); - g_assert (sinkpad); - link = gst_pad_link (h->srcpad, sinkpad); - g_assert_cmpint (link, ==, GST_PAD_LINK_OK); - g_free (priv->element_sinkpad_name); - priv->element_sinkpad_name = gst_pad_get_name (sinkpad); - - gst_object_unref (sinkpad); -} - -static void -gst_harness_setup_src_pad (GstHarness * h, - GstStaticPadTemplate * src_tmpl, const gchar * element_sinkpad_name) -{ - g_assert (src_tmpl); - g_assert (h->srcpad == NULL); - - /* sending pad */ - h->srcpad = gst_pad_new_from_static_template (src_tmpl, "src"); - g_assert (h->srcpad); - g_object_set_data (G_OBJECT (h->srcpad), HARNESS_KEY, h); - - gst_pad_set_query_function (h->srcpad, gst_harness_src_query); - gst_pad_set_event_function (h->srcpad, gst_harness_src_event); - - gst_pad_set_active (h->srcpad, TRUE); - - if (element_sinkpad_name) - gst_harness_link_element_sinkpad (h, element_sinkpad_name); -} - -static void -gst_harness_setup_sink_pad (GstHarness * h, - GstStaticPadTemplate * sink_tmpl, const gchar * element_srcpad_name) -{ - g_assert (sink_tmpl); - g_assert (h->sinkpad == NULL); - - /* receiving pad */ - h->sinkpad = gst_pad_new_from_static_template (sink_tmpl, "sink"); - g_assert (h->sinkpad); - g_object_set_data (G_OBJECT (h->sinkpad), HARNESS_KEY, h); - - gst_pad_set_chain_function (h->sinkpad, gst_harness_chain); - gst_pad_set_query_function (h->sinkpad, gst_harness_sink_query); - gst_pad_set_event_function (h->sinkpad, gst_harness_sink_event); - - gst_pad_set_active (h->sinkpad, TRUE); - - if (element_srcpad_name) - gst_harness_link_element_srcpad (h, element_srcpad_name); -} - -static void -check_element_type (GstElement * element, gboolean * has_sinkpad, - gboolean * has_srcpad) -{ - GstElementClass *element_class = GST_ELEMENT_GET_CLASS (element); - const GList *tmpl_list; - - *has_srcpad = element->numsrcpads > 0; - *has_sinkpad = element->numsinkpads > 0; - - tmpl_list = gst_element_class_get_pad_template_list (element_class); - - while (tmpl_list) { - GstPadTemplate *pad_tmpl = (GstPadTemplate *) tmpl_list->data; - tmpl_list = g_list_next (tmpl_list); - if (GST_PAD_TEMPLATE_DIRECTION (pad_tmpl) == GST_PAD_SRC) - *has_srcpad |= TRUE; - if (GST_PAD_TEMPLATE_DIRECTION (pad_tmpl) == GST_PAD_SINK) - *has_sinkpad |= TRUE; - } -} - -static void -turn_async_and_sync_off (GstElement * element) -{ - GObjectClass *class = G_OBJECT_GET_CLASS (element); - if (g_object_class_find_property (class, "async")) - g_object_set (element, "async", FALSE, NULL); - if (g_object_class_find_property (class, "sync")) - g_object_set (element, "sync", FALSE, NULL); -} - -static gboolean -gst_pad_is_request_pad (GstPad * pad) -{ - GstPadTemplate *temp; - gboolean is_request; - - if (pad == NULL) - return FALSE; - temp = gst_pad_get_pad_template (pad); - if (temp == NULL) - return FALSE; - is_request = GST_PAD_TEMPLATE_PRESENCE (temp) == GST_PAD_REQUEST; - gst_object_unref (temp); - return is_request; -} - -/** - * gst_harness_new_empty: (skip) - * - * Creates a new empty harness. Use gst_harness_add_element_full() to add - * an #GstElement to it. - * - * MT safe. - * - * Returns: (transfer full): a #GstHarness, or %NULL if the harness could - * not be created - * - * Since: 1.8 - */ -GstHarness * -gst_harness_new_empty (void) -{ - GstHarness *h; - GstHarnessPrivate *priv; - - h = g_new0 (GstHarness, 1); - g_assert (h != NULL); - h->priv = g_new0 (GstHarnessPrivate, 1); - priv = h->priv; - - GST_DEBUG ("about to create new harness %p", h); - priv->last_push_ts = GST_CLOCK_TIME_NONE; - priv->latency_min = 0; - priv->latency_max = GST_CLOCK_TIME_NONE; - priv->is_live = TRUE; - priv->drop_buffers = FALSE; - priv->testclock = GST_TEST_CLOCK_CAST (gst_test_clock_new ()); - - priv->buffer_queue = g_async_queue_new_full ( - (GDestroyNotify) gst_buffer_unref); - priv->src_event_queue = g_async_queue_new_full ( - (GDestroyNotify) gst_event_unref); - priv->sink_event_queue = g_async_queue_new_full ( - (GDestroyNotify) gst_event_unref); - - priv->propose_allocator = NULL; - gst_allocation_params_init (&priv->propose_allocation_params); - - g_mutex_init (&priv->blocking_push_mutex); - g_cond_init (&priv->blocking_push_cond); - g_mutex_init (&priv->priv_mutex); - - g_mutex_init (&priv->buf_or_eos_mutex); - g_cond_init (&priv->buf_or_eos_cond); - priv->eos_received = FALSE; - - priv->stress = g_ptr_array_new_with_free_func ( - (GDestroyNotify) gst_harness_stress_free); - - /* we have forwarding on as a default */ - gst_harness_set_forwarding (h, TRUE); - - return h; -} - -/** - * gst_harness_add_element_full: (skip) - * @h: a #GstHarness - * @element: a #GstElement to add to the harness (transfer none) - * @hsrc: (allow-none): a #GstStaticPadTemplate describing the harness srcpad. - * %NULL will not create a harness srcpad. - * @element_sinkpad_name: (allow-none): a #gchar with the name of the element - * sinkpad that is then linked to the harness srcpad. Can be a static or request - * or a sometimes pad that has been added. %NULL will not get/request a sinkpad - * from the element. (Like if the element is a src.) - * @hsink: (allow-none): a #GstStaticPadTemplate describing the harness sinkpad. - * %NULL will not create a harness sinkpad. - * @element_srcpad_name: (allow-none): a #gchar with the name of the element - * srcpad that is then linked to the harness sinkpad, similar to the - * @element_sinkpad_name. - * - * Adds a #GstElement to an empty #GstHarness - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_add_element_full (GstHarness * h, GstElement * element, - GstStaticPadTemplate * hsrc, const gchar * element_sinkpad_name, - GstStaticPadTemplate * hsink, const gchar * element_srcpad_name) -{ - GstClock *element_clock; - gboolean has_sinkpad, has_srcpad; - - g_return_if_fail (element != NULL); - g_return_if_fail (h->element == NULL); - - element_clock = GST_ELEMENT_CLOCK (element); - h->element = gst_object_ref (element); - check_element_type (element, &has_sinkpad, &has_srcpad); - - /* setup the loose srcpad linked to the element sinkpad */ - if (has_sinkpad) - gst_harness_setup_src_pad (h, hsrc, element_sinkpad_name); - - /* setup the loose sinkpad linked to the element srcpad */ - if (has_srcpad) - gst_harness_setup_sink_pad (h, hsink, element_srcpad_name); - - /* as a harness sink, we should not need sync and async */ - if (has_sinkpad && !has_srcpad) - turn_async_and_sync_off (h->element); - - if (h->srcpad != NULL) { - gboolean handled; - gchar *stream_id = g_strdup_printf ("%s-%p", - GST_OBJECT_NAME (h->element), h); - handled = gst_pad_push_event (h->srcpad, - gst_event_new_stream_start (stream_id)); - g_assert (handled); - g_free (stream_id); - } - - /* if the element already has a testclock attached, - we replace our own with it, if no clock we attach the testclock */ - if (element_clock) { - if (GST_IS_TEST_CLOCK (element_clock)) { - gst_object_replace ((GstObject **) & h->priv->testclock, - (GstObject *) GST_ELEMENT_CLOCK (element)); - } - } else { - gst_harness_use_testclock (h); - } - - /* don't start sources, they start producing data! */ - if (has_sinkpad) - gst_harness_play (h); - - gst_harness_element_ref (h); - - GST_DEBUG ("added element to harness %p " - "with element_srcpad_name (%p, %s, %s) and element_sinkpad_name (%p, %s, %s)", - h, h->srcpad, GST_DEBUG_PAD_NAME (h->srcpad), - h->sinkpad, GST_DEBUG_PAD_NAME (h->sinkpad)); -} - -/** - * gst_harness_new_full: (skip) - * @element: a #GstElement to attach the harness to (transfer none) - * @hsrc: (allow-none): a #GstStaticPadTemplate describing the harness srcpad. - * %NULL will not create a harness srcpad. - * @element_sinkpad_name: (allow-none): a #gchar with the name of the element - * sinkpad that is then linked to the harness srcpad. Can be a static or request - * or a sometimes pad that has been added. %NULL will not get/request a sinkpad - * from the element. (Like if the element is a src.) - * @hsink: (allow-none): a #GstStaticPadTemplate describing the harness sinkpad. - * %NULL will not create a harness sinkpad. - * @element_srcpad_name: (allow-none): a #gchar with the name of the element - * srcpad that is then linked to the harness sinkpad, similar to the - * @element_sinkpad_name. - * - * Creates a new harness. - * - * MT safe. - * - * Returns: (transfer full): a #GstHarness, or %NULL if the harness could - * not be created - * - * Since: 1.6 - */ -GstHarness * -gst_harness_new_full (GstElement * element, - GstStaticPadTemplate * hsrc, const gchar * element_sinkpad_name, - GstStaticPadTemplate * hsink, const gchar * element_srcpad_name) -{ - GstHarness *h; - h = gst_harness_new_empty (); - gst_harness_add_element_full (h, element, - hsrc, element_sinkpad_name, hsink, element_srcpad_name); - return h; -} - -/** - * gst_harness_new_with_element: (skip) - * @element: a #GstElement to attach the harness to (transfer none) - * @element_sinkpad_name: (allow-none): a #gchar with the name of the element - * sinkpad that is then linked to the harness srcpad. %NULL does not attach a - * sinkpad - * @element_srcpad_name: (allow-none): a #gchar with the name of the element - * srcpad that is then linked to the harness sinkpad. %NULL does not attach a - * srcpad - * - * Creates a new harness. Works in the same way as gst_harness_new_full(), only - * that generic padtemplates are used for the harness src and sinkpads, which - * will be sufficient in most usecases. - * - * MT safe. - * - * Returns: (transfer full): a #GstHarness, or %NULL if the harness could - * not be created - * - * Since: 1.6 - */ -GstHarness * -gst_harness_new_with_element (GstElement * element, - const gchar * element_sinkpad_name, const gchar * element_srcpad_name) -{ - return gst_harness_new_full (element, - &hsrctemplate, element_sinkpad_name, &hsinktemplate, element_srcpad_name); -} - -/** - * gst_harness_new_with_padnames: (skip) - * @element_name: a #gchar describing the #GstElement name - * @element_sinkpad_name: (allow-none): a #gchar with the name of the element - * sinkpad that is then linked to the harness srcpad. %NULL does not attach a - * sinkpad - * @element_srcpad_name: (allow-none): a #gchar with the name of the element - * srcpad that is then linked to the harness sinkpad. %NULL does not attach a - * srcpad - * - * Creates a new harness. Works like gst_harness_new_with_element(), - * except you specify the factoryname of the #GstElement - * - * MT safe. - * - * Returns: (transfer full): a #GstHarness, or %NULL if the harness could - * not be created - * - * Since: 1.6 - */ -GstHarness * -gst_harness_new_with_padnames (const gchar * element_name, - const gchar * element_sinkpad_name, const gchar * element_srcpad_name) -{ - GstHarness *h; - GstElement *element = gst_element_factory_make (element_name, NULL); - g_assert (element != NULL); - - h = gst_harness_new_with_element (element, element_sinkpad_name, - element_srcpad_name); - gst_object_unref (element); - return h; -} - -/** - * gst_harness_new_with_templates: (skip) - * @element_name: a #gchar describing the #GstElement name - * @hsrc: (allow-none): a #GstStaticPadTemplate describing the harness srcpad. - * %NULL will not create a harness srcpad. - * @hsink: (allow-none): a #GstStaticPadTemplate describing the harness sinkpad. - * %NULL will not create a harness sinkpad. - * - * Creates a new harness, like gst_harness_new_full(), except it - * assumes the #GstElement sinkpad is named "sink" and srcpad is named "src" - * - * MT safe. - * - * Returns: (transfer full): a #GstHarness, or %NULL if the harness could - * not be created - * - * Since: 1.6 - */ -GstHarness * -gst_harness_new_with_templates (const gchar * element_name, - GstStaticPadTemplate * hsrc, GstStaticPadTemplate * hsink) -{ - GstHarness *h; - GstElement *element = gst_element_factory_make (element_name, NULL); - g_assert (element != NULL); - - h = gst_harness_new_full (element, hsrc, "sink", hsink, "src"); - gst_object_unref (element); - return h; -} - -/** - * gst_harness_new: (skip) - * @element_name: a #gchar describing the #GstElement name - * - * Creates a new harness. Works like gst_harness_new_with_padnames(), except it - * assumes the #GstElement sinkpad is named "sink" and srcpad is named "src" - * - * MT safe. - * - * Returns: (transfer full): a #GstHarness, or %NULL if the harness could - * not be created - * - * Since: 1.6 - */ -GstHarness * -gst_harness_new (const gchar * element_name) -{ - return gst_harness_new_with_padnames (element_name, "sink", "src"); -} - -/** - * gst_harness_add_parse: (skip) - * @h: a #GstHarness - * @launchline: a #gchar describing a gst-launch type line - * - * Parses the @launchline and puts that in a #GstBin, - * and then attches the supplied #GstHarness to the bin. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_add_parse (GstHarness * h, const gchar * launchline) -{ - GstBin *bin; - gchar *desc; - GstPad *pad; - GstIterator *iter; - gboolean done = FALSE; - GError *error = NULL; - - g_return_if_fail (launchline != NULL); - - desc = g_strdup_printf ("bin.( %s )", launchline); - bin = - (GstBin *) gst_parse_launch_full (desc, NULL, GST_PARSE_FLAG_FATAL_ERRORS, - &error); - - if (G_UNLIKELY (error != NULL)) { - g_error ("Unable to create pipeline '%s': %s", desc, error->message); - } - g_free (desc); - - /* find pads and ghost them if necessary */ - if ((pad = gst_bin_find_unlinked_pad (bin, GST_PAD_SRC)) != NULL) { - gst_element_add_pad (GST_ELEMENT (bin), gst_ghost_pad_new ("src", pad)); - gst_object_unref (pad); - } - if ((pad = gst_bin_find_unlinked_pad (bin, GST_PAD_SINK)) != NULL) { - gst_element_add_pad (GST_ELEMENT (bin), gst_ghost_pad_new ("sink", pad)); - gst_object_unref (pad); - } - - iter = gst_bin_iterate_sinks (bin); - while (!done) { - GValue item = { 0, }; - - switch (gst_iterator_next (iter, &item)) { - case GST_ITERATOR_OK: - turn_async_and_sync_off (GST_ELEMENT (g_value_get_object (&item))); - g_value_reset (&item); - break; - case GST_ITERATOR_DONE: - done = TRUE; - break; - case GST_ITERATOR_RESYNC: - gst_iterator_resync (iter); - break; - case GST_ITERATOR_ERROR: - gst_object_unref (bin); - gst_iterator_free (iter); - g_return_if_reached (); - break; - } - } - gst_iterator_free (iter); - - gst_harness_add_element_full (h, GST_ELEMENT_CAST (bin), - &hsrctemplate, "sink", &hsinktemplate, "src"); - gst_object_unref (bin); -} - -/** - * gst_harness_new_parse: (skip) - * @launchline: a #gchar describing a gst-launch type line - * - * Creates a new harness, parsing the @launchline and putting that in a #GstBin, - * and then attches the harness to the bin. - * - * MT safe. - * - * Returns: (transfer full): a #GstHarness, or %NULL if the harness could - * not be created - * - * Since: 1.6 - */ -GstHarness * -gst_harness_new_parse (const gchar * launchline) -{ - GstHarness *h; - h = gst_harness_new_empty (); - gst_harness_add_parse (h, launchline); - return h; -} - -/** - * gst_harness_teardown: - * @h: a #GstHarness - * - * Tears down a @GstHarness, freeing all resources allocated using it. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_teardown (GstHarness * h) -{ - GstHarnessPrivate *priv = h->priv; - - if (priv->blocking_push_mode) { - g_mutex_lock (&priv->blocking_push_mutex); - priv->blocking_push_mode = FALSE; - g_cond_signal (&priv->blocking_push_cond); - g_mutex_unlock (&priv->blocking_push_mutex); - } - - if (h->src_harness) - gst_harness_teardown (h->src_harness); - h->src_harness = NULL; - - HARNESS_LOCK (h); - gst_object_replace ((GstObject **) & priv->sink_forward_pad, NULL); - HARNESS_UNLOCK (h); - - if (h->sink_harness) - gst_harness_teardown (h->sink_harness); - h->sink_harness = NULL; - - if (h->srcpad) { - if (gst_pad_is_request_pad (GST_PAD_PEER (h->srcpad))) - gst_element_release_request_pad (h->element, GST_PAD_PEER (h->srcpad)); - g_free (priv->element_sinkpad_name); - - gst_pad_set_active (h->srcpad, FALSE); - - /* Make sure our funcs are not called after harness is teared down since - * they try to access this harness through pad data */ - GST_PAD_STREAM_LOCK (h->srcpad); - gst_pad_set_event_function (h->srcpad, NULL); - gst_pad_set_query_function (h->srcpad, NULL); - GST_PAD_STREAM_UNLOCK (h->srcpad); - - gst_object_unref (h->srcpad); - } - h->srcpad = NULL; - - if (h->sinkpad) { - if (gst_pad_is_request_pad (GST_PAD_PEER (h->sinkpad))) - gst_element_release_request_pad (h->element, GST_PAD_PEER (h->sinkpad)); - g_free (priv->element_srcpad_name); - - gst_pad_set_active (h->sinkpad, FALSE); - - /* Make sure our funcs are not called after harness is teared down since - * they try to access this harness through pad data */ - GST_PAD_STREAM_LOCK (h->sinkpad); - gst_pad_set_chain_function (h->sinkpad, NULL); - gst_pad_set_event_function (h->sinkpad, NULL); - gst_pad_set_query_function (h->sinkpad, NULL); - GST_PAD_STREAM_UNLOCK (h->sinkpad); - - gst_object_unref (h->sinkpad); - } - h->sinkpad = NULL; - - if (priv->src_caps) - gst_caps_unref (priv->src_caps); - priv->src_caps = NULL; - - if (priv->sink_caps) - gst_caps_unref (priv->sink_caps); - priv->sink_caps = NULL; - - gst_object_replace ((GstObject **) & priv->propose_allocator, NULL); - gst_object_replace ((GstObject **) & priv->allocator, NULL); - gst_object_replace ((GstObject **) & priv->pool, NULL); - - if (priv->propose_allocation_metas) - g_array_unref (priv->propose_allocation_metas); - priv->propose_allocation_metas = NULL; - - /* if we hold the last ref, set to NULL */ - if (h->element != NULL && gst_harness_element_unref (h) == 0) { - gboolean state_change; - GstState state, pending; - state_change = gst_element_set_state (h->element, GST_STATE_NULL); - g_assert (state_change == GST_STATE_CHANGE_SUCCESS); - state_change = gst_element_get_state (h->element, &state, &pending, 0); - g_assert (state_change == GST_STATE_CHANGE_SUCCESS); - g_assert (state == GST_STATE_NULL); - } - - g_cond_clear (&priv->blocking_push_cond); - g_mutex_clear (&priv->blocking_push_mutex); - g_mutex_clear (&priv->priv_mutex); - - g_mutex_clear (&priv->buf_or_eos_mutex); - g_cond_clear (&priv->buf_or_eos_cond); - priv->eos_received = FALSE; - - g_async_queue_unref (priv->buffer_queue); - priv->buffer_queue = NULL; - g_async_queue_unref (priv->src_event_queue); - priv->src_event_queue = NULL; - g_async_queue_unref (priv->sink_event_queue); - priv->sink_event_queue = NULL; - - g_ptr_array_unref (priv->stress); - priv->stress = NULL; - - if (h->element) { - gst_object_unref (h->element); - h->element = NULL; - } - - gst_object_replace ((GstObject **) & priv->testclock, NULL); - - g_free (h->priv); - h->priv = NULL; - g_free (h); -} - -/** - * gst_harness_add_element_src_pad: - * @h: a #GstHarness - * @srcpad: a #GstPad to link to the harness sinkpad - * - * Links the specified #GstPad the @GstHarness sinkpad. This can be useful if - * perhaps the srcpad did not exist at the time of creating the harness, - * like a demuxer that provides a sometimes-pad after receiving data. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_add_element_src_pad (GstHarness * h, GstPad * srcpad) -{ - GstHarnessPrivate *priv = h->priv; - GstPadLinkReturn link; - if (h->sinkpad == NULL) - gst_harness_setup_sink_pad (h, &hsinktemplate, NULL); - link = gst_pad_link (srcpad, h->sinkpad); - g_assert_cmpint (link, ==, GST_PAD_LINK_OK); - g_free (priv->element_srcpad_name); - priv->element_srcpad_name = gst_pad_get_name (srcpad); -} - -/** - * gst_harness_add_element_sink_pad: - * @h: a #GstHarness - * @sinkpad: a #GstPad to link to the harness srcpad - * - * Links the specified #GstPad the @GstHarness srcpad. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_add_element_sink_pad (GstHarness * h, GstPad * sinkpad) -{ - GstHarnessPrivate *priv = h->priv; - GstPadLinkReturn link; - if (h->srcpad == NULL) - gst_harness_setup_src_pad (h, &hsrctemplate, NULL); - link = gst_pad_link (h->srcpad, sinkpad); - g_assert_cmpint (link, ==, GST_PAD_LINK_OK); - g_free (priv->element_sinkpad_name); - priv->element_sinkpad_name = gst_pad_get_name (sinkpad); -} - -/** - * gst_harness_set_src_caps: - * @h: a #GstHarness - * @caps: (transfer full): a #GstCaps to set on the harness srcpad - * - * Sets the @GstHarness srcpad caps. This must be done before any buffers - * can legally be pushed from the harness to the element. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_set_src_caps (GstHarness * h, GstCaps * caps) -{ - GstHarnessPrivate *priv = h->priv; - GstSegment segment; - gboolean handled; - - handled = gst_pad_push_event (h->srcpad, gst_event_new_caps (caps)); - g_assert (handled); - gst_caps_take (&priv->src_caps, caps); - - gst_segment_init (&segment, GST_FORMAT_TIME); - handled = gst_pad_push_event (h->srcpad, gst_event_new_segment (&segment)); - g_assert (handled); -} - -/** - * gst_harness_set_sink_caps: - * @h: a #GstHarness - * @caps: (transfer full): a #GstCaps to set on the harness sinkpad - * - * Sets the @GstHarness sinkpad caps. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_set_sink_caps (GstHarness * h, GstCaps * caps) -{ - GstHarnessPrivate *priv = h->priv; - - gst_caps_take (&priv->sink_caps, caps); - gst_pad_push_event (h->sinkpad, gst_event_new_reconfigure ()); -} - -/** - * gst_harness_set_caps: - * @h: a #GstHarness - * @in: (transfer full): a #GstCaps to set on the harness srcpad - * @out: (transfer full): a #GstCaps to set on the harness sinkpad - * - * Sets the @GstHarness srcpad and sinkpad caps. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_set_caps (GstHarness * h, GstCaps * in, GstCaps * out) -{ - gst_harness_set_sink_caps (h, out); - gst_harness_set_src_caps (h, in); -} - -/** - * gst_harness_set_src_caps_str: - * @h: a #GstHarness - * @str: a @gchar describing a #GstCaps to set on the harness srcpad - * - * Sets the @GstHarness srcpad caps using a string. This must be done before - * any buffers can legally be pushed from the harness to the element. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_set_src_caps_str (GstHarness * h, const gchar * str) -{ - gst_harness_set_src_caps (h, gst_caps_from_string (str)); -} - -/** - * gst_harness_set_sink_caps_str: - * @h: a #GstHarness - * @str: a @gchar describing a #GstCaps to set on the harness sinkpad - * - * Sets the @GstHarness sinkpad caps using a string. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_set_sink_caps_str (GstHarness * h, const gchar * str) -{ - gst_harness_set_sink_caps (h, gst_caps_from_string (str)); -} - -/** - * gst_harness_set_caps_str: - * @h: a #GstHarness - * @in: a @gchar describing a #GstCaps to set on the harness srcpad - * @out: a @gchar describing a #GstCaps to set on the harness sinkpad - * - * Sets the @GstHarness srcpad and sinkpad caps using strings. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_set_caps_str (GstHarness * h, const gchar * in, const gchar * out) -{ - gst_harness_set_sink_caps_str (h, out); - gst_harness_set_src_caps_str (h, in); -} - -/** - * gst_harness_use_systemclock: - * @h: a #GstHarness - * - * Sets the system #GstClock on the @GstHarness #GstElement - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_use_systemclock (GstHarness * h) -{ - GstClock *clock = gst_system_clock_obtain (); - g_assert (clock != NULL); - gst_element_set_clock (h->element, clock); - gst_object_unref (clock); -} - -/** - * gst_harness_use_testclock: - * @h: a #GstHarness - * - * Sets the #GstTestClock on the #GstHarness #GstElement - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_use_testclock (GstHarness * h) -{ - gst_element_set_clock (h->element, GST_CLOCK_CAST (h->priv->testclock)); -} - -/** - * gst_harness_get_testclock: - * @h: a #GstHarness - * - * Get the #GstTestClock. Useful if specific operations on the testclock is - * needed. - * - * MT safe. - * - * Returns: (transfer full): a #GstTestClock, or %NULL if the testclock is not - * present. - * - * Since: 1.6 - */ -GstTestClock * -gst_harness_get_testclock (GstHarness * h) -{ - return gst_object_ref (h->priv->testclock); -} - -/** - * gst_harness_set_time: - * @h: a #GstHarness - * @time: a #GstClockTime to advance the clock to - * - * Advance the #GstTestClock to a specific time. - * - * MT safe. - * - * Returns: a @gboolean %TRUE if the time could be set. %FALSE if not. - * - * Since: 1.6 - */ -gboolean -gst_harness_set_time (GstHarness * h, GstClockTime time) -{ - gst_test_clock_set_time (h->priv->testclock, time); - return TRUE; -} - -/** - * gst_harness_wait_for_clock_id_waits: - * @h: a #GstHarness - * @waits: a #guint describing the numbers of #GstClockID registered with - * the #GstTestClock - * @timeout: a #guint describing how many seconds to wait for @waits to be true - * - * Waits for @timeout seconds until @waits number of #GstClockID waits is - * registered with the #GstTestClock. Useful for writing deterministic tests, - * where you want to make sure that an expected number of waits have been - * reached. - * - * MT safe. - * - * Returns: a @gboolean %TRUE if the waits have been registered, %FALSE if not. - * (Could be that it timed out waiting or that more waits than waits was found) - * - * Since: 1.6 - */ -gboolean -gst_harness_wait_for_clock_id_waits (GstHarness * h, guint waits, guint timeout) -{ - return gst_test_clock_timed_wait_for_multiple_pending_ids (h->priv->testclock, - waits, timeout * 1000, NULL); -} - -/** - * gst_harness_crank_single_clock_wait: - * @h: a #GstHarness - * - * A "crank" consists of three steps: - * 1: Wait for a #GstClockID to be registered with the #GstTestClock. - * 2: Advance the #GstTestClock to the time the #GstClockID is waiting for. - * 3: Release the #GstClockID wait. - * Together, this provides an easy way to not have to think about the details - * around clocks and time, but still being able to write deterministic tests - * that are dependent on this. A "crank" can be though of as the notion of - * manually driving the clock forward to its next logical step. - * - * MT safe. - * - * Returns: a @gboolean %TRUE if the "crank" was successful, %FALSE if not. - * - * Since: 1.6 - */ -gboolean -gst_harness_crank_single_clock_wait (GstHarness * h) -{ - return gst_test_clock_crank (h->priv->testclock); -} - -/** - * gst_harness_crank_multiple_clock_waits: - * @h: a #GstHarness - * @waits: a #guint describing the number of #GstClockIDs to crank - * - * Similar to gst_harness_crank_single_clock_wait(), this is the function to use - * if your harnessed element(s) are using more then one gst_clock_id_wait. - * Failing to do so can (and will) make it racy which #GstClockID you actually - * are releasing, where as this function will process all the waits at the - * same time, ensuring that one thread can't register another wait before - * both are released. - * - * MT safe. - * - * Returns: a @gboolean %TRUE if the "crank" was successful, %FALSE if not. - * - * Since: 1.6 - */ -gboolean -gst_harness_crank_multiple_clock_waits (GstHarness * h, guint waits) -{ - GstTestClock *testclock = h->priv->testclock; - GList *pending; - guint processed; - - gst_test_clock_wait_for_multiple_pending_ids (testclock, waits, &pending); - gst_harness_set_time (h, gst_test_clock_id_list_get_latest_time (pending)); - processed = gst_test_clock_process_id_list (testclock, pending); - - g_list_free_full (pending, gst_clock_id_unref); - return processed == waits; -} - -/** - * gst_harness_play: - * @h: a #GstHarness - * - * This will set the harnessed #GstElement to %GST_STATE_PLAYING. - * #GstElements without a sink-#GstPad and with the %GST_ELEMENT_FLAG_SOURCE - * flag set is considered a src #GstElement - * Non-src #GstElements (like sinks and filters) are automatically set to - * playing by the #GstHarness, but src #GstElements are not to avoid them - * starting to produce buffers. - * Hence, for src #GstElement you must call gst_harness_play() explicitly. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_play (GstHarness * h) -{ - GstState state, pending; - gboolean state_change; - state_change = gst_element_set_state (h->element, GST_STATE_PLAYING); - g_assert_cmpint (GST_STATE_CHANGE_SUCCESS, ==, state_change); - state_change = gst_element_get_state (h->element, &state, &pending, 0); - g_assert_cmpint (GST_STATE_CHANGE_SUCCESS, ==, state_change); - g_assert_cmpint (GST_STATE_PLAYING, ==, state); -} - -/** - * gst_harness_set_blocking_push_mode: - * @h: a #GstHarness - * - * Setting this will make the harness block in the chain-function, and - * then release when gst_harness_pull() or gst_harness_try_pull() is called. - * Can be useful when wanting to control a src-element that is not implementing - * gst_clock_id_wait() so it can't be controlled by the #GstTestClock, since - * it otherwise would produce buffers as fast as possible. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_set_blocking_push_mode (GstHarness * h) -{ - GstHarnessPrivate *priv = h->priv; - priv->blocking_push_mode = TRUE; -} - -/** - * gst_harness_set_forwarding: - * @h: a #GstHarness - * @forwarding: a #gboolean to enable/disable forwarding - * - * As a convenience, a src-harness will forward %GST_EVENT_STREAM_START, - * %GST_EVENT_CAPS and %GST_EVENT_SEGMENT to the main-harness if forwarding - * is enabled, and forward any sticky-events from the main-harness to - * the sink-harness. It will also forward the %GST_QUERY_ALLOCATION. - * - * If forwarding is disabled, the user will have to either manually push - * these events from the src-harness using gst_harness_src_push_event(), or - * create and push them manually. While this will allow full control and - * inspection of these events, for the most cases having forwarding enabled - * will be sufficient when writing a test where the src-harness' main function - * is providing data for the main-harness. - * - * Forwarding is enabled by default. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_set_forwarding (GstHarness * h, gboolean forwarding) -{ - GstHarnessPrivate *priv = h->priv; - priv->forwarding = forwarding; - if (h->src_harness) - gst_harness_set_forwarding (h->src_harness, forwarding); - if (h->sink_harness) - gst_harness_set_forwarding (h->sink_harness, forwarding); -} - -/* -* Call with HARNESS_LOCK -*/ -static void -gst_harness_set_forward_pad (GstHarness * h, GstPad * fwdpad) -{ - gst_object_replace ((GstObject **) & h->priv->sink_forward_pad, - (GstObject *) fwdpad); -} - -/** - * gst_harness_create_buffer: - * @h: a #GstHarness - * @size: a #gsize specifying the size of the buffer - * - * Allocates a buffer using a #GstBufferPool if present, or else using the - * configured #GstAllocator and #GstAllocationParams - * - * MT safe. - * - * Returns: a #GstBuffer of size @size - * - * Since: 1.6 - */ -GstBuffer * -gst_harness_create_buffer (GstHarness * h, gsize size) -{ - GstHarnessPrivate *priv = h->priv; - GstBuffer *ret = NULL; - GstFlowReturn flow; - - if (gst_pad_check_reconfigure (h->srcpad)) - gst_harness_negotiate (h); - - if (priv->pool) { - flow = gst_buffer_pool_acquire_buffer (priv->pool, &ret, NULL); - g_assert_cmpint (flow, ==, GST_FLOW_OK); - if (gst_buffer_get_size (ret) != size) { - GST_DEBUG ("use fallback, pool is configured with a different size (%" - G_GSIZE_FORMAT " != %" G_GSIZE_FORMAT ")", - size, gst_buffer_get_size (ret)); - gst_buffer_unref (ret); - ret = NULL; - } - } - - if (!ret) - ret = - gst_buffer_new_allocate (priv->allocator, size, - &priv->allocation_params); - - g_assert (ret != NULL); - return ret; -} - -/** - * gst_harness_push: - * @h: a #GstHarness - * @buffer: (transfer full): a #GstBuffer to push - * - * Pushes a #GstBuffer on the #GstHarness srcpad. The standard way of - * interacting with an harnessed element. - * - * MT safe. - * - * Returns: a #GstFlowReturn with the result from the push - * - * Since: 1.6 - */ -GstFlowReturn -gst_harness_push (GstHarness * h, GstBuffer * buffer) -{ - GstHarnessPrivate *priv = h->priv; - g_assert (buffer != NULL); - priv->last_push_ts = GST_BUFFER_TIMESTAMP (buffer); - return gst_pad_push (h->srcpad, buffer); -} - -/** - * gst_harness_pull: - * @h: a #GstHarness - * - * Pulls a #GstBuffer from the #GAsyncQueue on the #GstHarness sinkpad. The pull - * will timeout in 60 seconds. This is the standard way of getting a buffer - * from a harnessed #GstElement. - * - * MT safe. - * - * Returns: (transfer full): a #GstBuffer or %NULL if timed out. - * - * Since: 1.6 - */ -GstBuffer * -gst_harness_pull (GstHarness * h) -{ - GstHarnessPrivate *priv = h->priv; - GstBuffer *buf = (GstBuffer *) g_async_queue_timeout_pop (priv->buffer_queue, - G_USEC_PER_SEC * 60); - - if (priv->blocking_push_mode) { - g_mutex_lock (&priv->blocking_push_mutex); - g_cond_signal (&priv->blocking_push_cond); - g_mutex_unlock (&priv->blocking_push_mutex); - } - - return buf; -} - -/** - * gst_harness_pull_until_eos: - * @h: a #GstHarness - * @buf: (out) (transfer full): A #GstBuffer, or %NULL if EOS or timeout occures - * first. - * - * Pulls a #GstBuffer from the #GAsyncQueue on the #GstHarness sinkpad. The pull - * will block until an EOS event is received, or timeout in 60 seconds. - * MT safe. - * - * Returns: %TRUE on success, %FALSE on timeout. - * - * Since: 1.18 - */ -gboolean -gst_harness_pull_until_eos (GstHarness * h, GstBuffer ** buf) -{ - GstHarnessPrivate *priv = h->priv; - gboolean success = TRUE; - gint64 end_time = g_get_monotonic_time () + 60 * G_TIME_SPAN_SECOND; - - g_mutex_lock (&priv->buf_or_eos_mutex); - while (success) { - *buf = g_async_queue_try_pop (priv->buffer_queue); - if (*buf || priv->eos_received) - break; - success = g_cond_wait_until (&priv->buf_or_eos_cond, - &priv->buf_or_eos_mutex, end_time); - } - g_mutex_unlock (&priv->buf_or_eos_mutex); - - return success; -} - -/** - * gst_harness_try_pull: - * @h: a #GstHarness - * - * Pulls a #GstBuffer from the #GAsyncQueue on the #GstHarness sinkpad. Unlike - * gst_harness_pull this will not wait for any buffers if not any are present, - * and return %NULL straight away. - * - * MT safe. - * - * Returns: (transfer full): a #GstBuffer or %NULL if no buffers are present in the #GAsyncQueue - * - * Since: 1.6 - */ -GstBuffer * -gst_harness_try_pull (GstHarness * h) -{ - GstHarnessPrivate *priv = h->priv; - GstBuffer *buf = (GstBuffer *) g_async_queue_try_pop (priv->buffer_queue); - - if (priv->blocking_push_mode) { - g_mutex_lock (&priv->blocking_push_mutex); - g_cond_signal (&priv->blocking_push_cond); - g_mutex_unlock (&priv->blocking_push_mutex); - } - - return buf; -} - -/** - * gst_harness_push_and_pull: - * @h: a #GstHarness - * @buffer: (transfer full): a #GstBuffer to push - * - * Basically a gst_harness_push and a gst_harness_pull in one line. Reflects - * the fact that you often want to do exactly this in your test: Push one buffer - * in, and inspect the outcome. - * - * MT safe. - * - * Returns: (transfer full): a #GstBuffer or %NULL if timed out. - * - * Since: 1.6 - */ -GstBuffer * -gst_harness_push_and_pull (GstHarness * h, GstBuffer * buffer) -{ - gst_harness_push (h, buffer); - return gst_harness_pull (h); -} - -/** - * gst_harness_buffers_received: - * @h: a #GstHarness - * - * The total number of #GstBuffers that has arrived on the #GstHarness sinkpad. - * This number includes buffers that have been dropped as well as buffers - * that have already been pulled out. - * - * MT safe. - * - * Returns: a #guint number of buffers received - * - * Since: 1.6 - */ -guint -gst_harness_buffers_received (GstHarness * h) -{ - GstHarnessPrivate *priv = h->priv; - return g_atomic_int_get (&priv->recv_buffers); -} - -/** - * gst_harness_buffers_in_queue: - * @h: a #GstHarness - * - * The number of #GstBuffers currently in the #GstHarness sinkpad #GAsyncQueue - * - * MT safe. - * - * Returns: a #guint number of buffers in the queue - * - * Since: 1.6 - */ -guint -gst_harness_buffers_in_queue (GstHarness * h) -{ - GstHarnessPrivate *priv = h->priv; - return g_async_queue_length (priv->buffer_queue); -} - -/** - * gst_harness_set_drop_buffers: - * @h: a #GstHarness - * @drop_buffers: a #gboolean specifying to drop outgoing buffers or not - * - * When set to %TRUE, instead of placing the buffers arriving from the harnessed - * #GstElement inside the sinkpads #GAsyncQueue, they are instead unreffed. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_set_drop_buffers (GstHarness * h, gboolean drop_buffers) -{ - GstHarnessPrivate *priv = h->priv; - priv->drop_buffers = drop_buffers; -} - -/** - * gst_harness_take_all_data_as_buffer: - * @h: a #GstHarness - * - * Pulls all pending data from the harness and returns it as a single buffer. - * - * Returns: (transfer full): the data as a buffer. Unref with gst_buffer_unref() - * when no longer needed. - * - * Since: 1.14 - */ -GstBuffer * -gst_harness_take_all_data_as_buffer (GstHarness * h) -{ - GstHarnessPrivate *priv; - GstBuffer *ret, *buf; - - g_return_val_if_fail (h != NULL, NULL); - - priv = h->priv; - - g_async_queue_lock (priv->buffer_queue); - - ret = g_async_queue_try_pop_unlocked (priv->buffer_queue); - - if (ret == NULL) { - ret = gst_buffer_new (); - } else { - /* buffer appending isn't very efficient for larger numbers of buffers - * or lots of memories, but this function is not performance critical and - * we can still improve it if and when the need arises. For now KISS. */ - while ((buf = g_async_queue_try_pop_unlocked (priv->buffer_queue))) - ret = gst_buffer_append (ret, buf); - } - - g_async_queue_unlock (priv->buffer_queue); - - return ret; -} - -/** - * gst_harness_take_all_data: (skip) - * @h: a #GstHarness - * @size: (out): the size of the data in bytes - * - * Pulls all pending data from the harness and returns it as a single - * data slice. - * - * Returns: (transfer full): a pointer to the data, newly allocated. Free - * with g_free() when no longer needed. Will return %NULL if there is no - * data. - * - * Since: 1.14 - */ -guint8 * -gst_harness_take_all_data (GstHarness * h, gsize * size) -{ - GstBuffer *buf; - guint8 *data = NULL; - - g_return_val_if_fail (h != NULL, NULL); - g_return_val_if_fail (size != NULL, NULL); - - buf = gst_harness_take_all_data_as_buffer (h); - gst_buffer_extract_dup (buf, 0, -1, (gpointer *) & data, size); - gst_buffer_unref (buf); - return data; -} - -/** - * gst_harness_take_all_data_as_bytes: (rename-to gst_harness_take_all_data) - * @h: a #GstHarness - * - * Pulls all pending data from the harness and returns it as a single #GBytes. - * - * Returns: (transfer full): a pointer to the data, newly allocated. Free - * with g_free() when no longer needed. - * - * Since: 1.14 - */ -GBytes * -gst_harness_take_all_data_as_bytes (GstHarness * h) -{ - guint8 *data; - gsize size = 0; - - g_return_val_if_fail (h != NULL, NULL); - - data = gst_harness_take_all_data (h, &size); - return g_bytes_new_take (data, size); -} - - -/** - * gst_harness_dump_to_file: - * @h: a #GstHarness - * @filename: a #gchar with a the name of a file - * - * Allows you to dump the #GstBuffers the #GstHarness sinkpad #GAsyncQueue - * to a file. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_dump_to_file (GstHarness * h, const gchar * filename) -{ - GError *err = NULL; - gpointer data; - gsize size; - - data = gst_harness_take_all_data (h, &size); - if (!g_file_set_contents (filename, data ? data : "", size, &err)) { - g_error ("GstHarness: Failed to write data to file: %s", err->message); - g_clear_error (&err); - } - g_free (data); -} - -/** - * gst_harness_get_last_pushed_timestamp: - * @h: a #GstHarness - * - * Get the timestamp of the last #GstBuffer pushed on the #GstHarness srcpad, - * typically with gst_harness_push or gst_harness_push_from_src. - * - * MT safe. - * - * Returns: a #GstClockTime with the timestamp or %GST_CLOCK_TIME_NONE if no - * #GstBuffer has been pushed on the #GstHarness srcpad - * - * Since: 1.6 - */ -GstClockTime -gst_harness_get_last_pushed_timestamp (GstHarness * h) -{ - GstHarnessPrivate *priv = h->priv; - return priv->last_push_ts; -} - -/** - * gst_harness_push_event: - * @h: a #GstHarness - * @event: a #GstEvent to push - * - * Pushes an #GstEvent on the #GstHarness srcpad. - * - * MT safe. - * - * Returns: a #gboolean with the result from the push - * - * Since: 1.6 - */ -gboolean -gst_harness_push_event (GstHarness * h, GstEvent * event) -{ - return gst_pad_push_event (h->srcpad, event); -} - -/** - * gst_harness_pull_event: - * @h: a #GstHarness - * - * Pulls an #GstEvent from the #GAsyncQueue on the #GstHarness sinkpad. - * Timeouts after 60 seconds similar to gst_harness_pull. - * - * MT safe. - * - * Returns: a #GstEvent or %NULL if timed out. - * - * Since: 1.6 - */ -GstEvent * -gst_harness_pull_event (GstHarness * h) -{ - GstHarnessPrivate *priv = h->priv; - return (GstEvent *) g_async_queue_timeout_pop (priv->sink_event_queue, - G_USEC_PER_SEC * 60); -} - -/** - * gst_harness_try_pull_event: - * @h: a #GstHarness - * - * Pulls an #GstEvent from the #GAsyncQueue on the #GstHarness sinkpad. - * See gst_harness_try_pull for details. - * - * MT safe. - * - * Returns: a #GstEvent or %NULL if no buffers are present in the #GAsyncQueue - * - * Since: 1.6 - */ -GstEvent * -gst_harness_try_pull_event (GstHarness * h) -{ - GstHarnessPrivate *priv = h->priv; - return (GstEvent *) g_async_queue_try_pop (priv->sink_event_queue); -} - -/** - * gst_harness_events_received: - * @h: a #GstHarness - * - * The total number of #GstEvents that has arrived on the #GstHarness sinkpad - * This number includes events handled by the harness as well as events - * that have already been pulled out. - * - * MT safe. - * - * Returns: a #guint number of events received - * - * Since: 1.6 - */ -guint -gst_harness_events_received (GstHarness * h) -{ - GstHarnessPrivate *priv = h->priv; - return g_atomic_int_get (&priv->recv_events); -} - -/** - * gst_harness_events_in_queue: - * @h: a #GstHarness - * - * The number of #GstEvents currently in the #GstHarness sinkpad #GAsyncQueue - * - * MT safe. - * - * Returns: a #guint number of events in the queue - * - * Since: 1.6 - */ -guint -gst_harness_events_in_queue (GstHarness * h) -{ - GstHarnessPrivate *priv = h->priv; - return g_async_queue_length (priv->sink_event_queue); -} - -/** - * gst_harness_push_upstream_event: - * @h: a #GstHarness - * @event: a #GstEvent to push - * - * Pushes an #GstEvent on the #GstHarness sinkpad. - * - * MT safe. - * - * Returns: a #gboolean with the result from the push - * - * Since: 1.6 - */ -gboolean -gst_harness_push_upstream_event (GstHarness * h, GstEvent * event) -{ - g_return_val_if_fail (event != NULL, FALSE); - g_return_val_if_fail (GST_EVENT_IS_UPSTREAM (event), FALSE); - - return gst_pad_push_event (h->sinkpad, event); -} - -/** - * gst_harness_pull_upstream_event: - * @h: a #GstHarness - * - * Pulls an #GstEvent from the #GAsyncQueue on the #GstHarness srcpad. - * Timeouts after 60 seconds similar to gst_harness_pull. - * - * MT safe. - * - * Returns: a #GstEvent or %NULL if timed out. - * - * Since: 1.6 - */ -GstEvent * -gst_harness_pull_upstream_event (GstHarness * h) -{ - GstHarnessPrivate *priv = h->priv; - return (GstEvent *) g_async_queue_timeout_pop (priv->src_event_queue, - G_USEC_PER_SEC * 60); -} - -/** - * gst_harness_try_pull_upstream_event: - * @h: a #GstHarness - * - * Pulls an #GstEvent from the #GAsyncQueue on the #GstHarness srcpad. - * See gst_harness_try_pull for details. - * - * MT safe. - * - * Returns: a #GstEvent or %NULL if no buffers are present in the #GAsyncQueue - * - * Since: 1.6 - */ -GstEvent * -gst_harness_try_pull_upstream_event (GstHarness * h) -{ - GstHarnessPrivate *priv = h->priv; - return (GstEvent *) g_async_queue_try_pop (priv->src_event_queue); -} - -/** - * gst_harness_upstream_events_received: - * @h: a #GstHarness - * - * The total number of #GstEvents that has arrived on the #GstHarness srcpad - * This number includes events handled by the harness as well as events - * that have already been pulled out. - * - * MT safe. - * - * Returns: a #guint number of events received - * - * Since: 1.6 - */ -guint -gst_harness_upstream_events_received (GstHarness * h) -{ - GstHarnessPrivate *priv = h->priv; - return g_atomic_int_get (&priv->recv_upstream_events); -} - -/** - * gst_harness_upstream_events_in_queue: - * @h: a #GstHarness - * - * The number of #GstEvents currently in the #GstHarness srcpad #GAsyncQueue - * - * MT safe. - * - * Returns: a #guint number of events in the queue - * - * Since: 1.6 - */ -guint -gst_harness_upstream_events_in_queue (GstHarness * h) -{ - GstHarnessPrivate *priv = h->priv; - return g_async_queue_length (priv->src_event_queue); -} - -/** - * gst_harness_query_latency: - * @h: a #GstHarness - * - * Get the min latency reported by any harnessed #GstElement. - * - * MT safe. - * - * Returns: a #GstClockTime with min latency - * - * Since: 1.6 - */ -GstClockTime -gst_harness_query_latency (GstHarness * h) -{ - GstQuery *query; - gboolean is_live; - GstClockTime min = GST_CLOCK_TIME_NONE; - GstClockTime max; - - query = gst_query_new_latency (); - - if (gst_pad_peer_query (h->sinkpad, query)) { - gst_query_parse_latency (query, &is_live, &min, &max); - } - gst_query_unref (query); - - return min; -} - -/** - * gst_harness_set_upstream_latency: - * @h: a #GstHarness - * @latency: a #GstClockTime specifying the latency - * - * Sets the min latency reported by #GstHarness when receiving a latency-query - * - * Since: 1.6 - */ -void -gst_harness_set_upstream_latency (GstHarness * h, GstClockTime latency) -{ - g_return_if_fail (GST_CLOCK_TIME_IS_VALID (latency)); - - h->priv->latency_min = latency; -} - -/** - * gst_harness_set_live: - * @h: a #GstHarness - * @is_live: %TRUE for live, %FALSE for non-live - * - * Sets the liveness reported by #GstHarness when receiving a latency-query. - * The default is %TRUE. - * - * Since: 1.20 - */ -void -gst_harness_set_live (GstHarness * h, gboolean is_live) -{ - GstHarnessPrivate *priv = h->priv; - priv->is_live = is_live; -} - -/** - * gst_harness_get_allocator: - * @h: a #GstHarness - * @allocator: (out) (allow-none) (transfer none): the #GstAllocator used - * @params: (out) (allow-none) (transfer full): the #GstAllocationParams of - * @allocator - * - * Gets the @allocator and its @params that has been decided to use after an - * allocation query. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_get_allocator (GstHarness * h, GstAllocator ** allocator, - GstAllocationParams * params) -{ - GstHarnessPrivate *priv = h->priv; - if (allocator) - *allocator = priv->allocator; - if (params) - *params = priv->allocation_params; -} - - -/** - * gst_harness_set_propose_allocator: - * @h: a #GstHarness - * @allocator: (allow-none) (transfer full): a #GstAllocator - * @params: (allow-none) (transfer none): a #GstAllocationParams - * - * Sets the @allocator and @params to propose when receiving an allocation - * query. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_set_propose_allocator (GstHarness * h, GstAllocator * allocator, - const GstAllocationParams * params) -{ - GstHarnessPrivate *priv = h->priv; - if (allocator) - priv->propose_allocator = allocator; - if (params) - priv->propose_allocation_params = *params; -} - -/** - * gst_harness_add_propose_allocation_meta: - * @h: a #GstHarness - * @api: a metadata API - * @params: (allow-none) (transfer none): API specific parameters - * - * Add api with params as one of the supported metadata API to propose when - * receiving an allocation query. - * - * MT safe. - * - * Since: 1.16 - */ -void -gst_harness_add_propose_allocation_meta (GstHarness * h, GType api, - const GstStructure * params) -{ - GstHarnessPrivate *priv = h->priv; - ProposeMeta meta; - - meta.api = api; - meta.params = params ? gst_structure_copy (params) : NULL; - - if (!priv->propose_allocation_metas) { - priv->propose_allocation_metas = - g_array_new (FALSE, FALSE, sizeof (ProposeMeta)); - g_array_set_clear_func (priv->propose_allocation_metas, - (GDestroyNotify) propose_meta_clear); - } - g_array_append_val (priv->propose_allocation_metas, meta); -} - -/** - * gst_harness_add_src_harness: - * @h: a #GstHarness - * @src_harness: (transfer full): a #GstHarness to be added as a src-harness. - * @has_clock_wait: a #gboolean specifying if the #GstElement uses - * gst_clock_wait_id internally. - * - * A src-harness is a great way of providing the #GstHarness with data. - * By adding a src-type #GstElement, it is then easy to use functions like - * gst_harness_push_from_src or gst_harness_src_crank_and_push_many - * to provide your harnessed element with input. The @has_clock_wait variable - * is a great way to control you src-element with, in that you can have it - * produce a buffer for you by simply cranking the clock, and not have it - * spin out of control producing buffers as fast as possible. - * - * If a src-harness already exists it will be replaced. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_add_src_harness (GstHarness * h, - GstHarness * src_harness, gboolean has_clock_wait) -{ - if (h->src_harness) - gst_harness_teardown (h->src_harness); - h->src_harness = src_harness; - - HARNESS_LOCK (h->src_harness); - gst_harness_set_forward_pad (h->src_harness, h->srcpad); - HARNESS_UNLOCK (h->src_harness); - - h->src_harness->priv->has_clock_wait = has_clock_wait; - gst_harness_set_forwarding (h->src_harness, h->priv->forwarding); -} - -/** - * gst_harness_add_src: - * @h: a #GstHarness - * @src_element_name: a #gchar with the name of a #GstElement - * @has_clock_wait: a #gboolean specifying if the #GstElement uses - * gst_clock_wait_id internally. - * - * Similar to gst_harness_add_src_harness, this is a convenience to - * directly create a src-harness using the @src_element_name name specified. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_add_src (GstHarness * h, - const gchar * src_element_name, gboolean has_clock_wait) -{ - GstHarness *src_harness = gst_harness_new (src_element_name); - gst_harness_add_src_harness (h, src_harness, has_clock_wait); -} - -/** - * gst_harness_add_src_parse: - * @h: a #GstHarness - * @launchline: a #gchar describing a gst-launch type line - * @has_clock_wait: a #gboolean specifying if the #GstElement uses - * gst_clock_wait_id internally. - * - * Similar to gst_harness_add_src, this allows you to specify a launch-line, - * which can be useful for both having more then one #GstElement acting as your - * src (Like a src producing raw buffers, and then an encoder, providing encoded - * data), but also by allowing you to set properties like "is-live" directly on - * the elements. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_add_src_parse (GstHarness * h, - const gchar * launchline, gboolean has_clock_wait) -{ - GstHarness *src_harness = gst_harness_new_parse (launchline); - gst_harness_add_src_harness (h, src_harness, has_clock_wait); -} - -/** - * gst_harness_push_from_src: - * @h: a #GstHarness - * - * Transfer data from the src-#GstHarness to the main-#GstHarness. It consists - * of 4 steps: - * 1: Make sure the src is started. (see: gst_harness_play) - * 2: Crank the clock (see: gst_harness_crank_single_clock_wait) - * 3: Pull a #GstBuffer from the src-#GstHarness (see: gst_harness_pull) - * 4: Push the same #GstBuffer into the main-#GstHarness (see: gst_harness_push) - * - * MT safe. - * - * Returns: a #GstFlowReturn with the result of the push - * - * Since: 1.6 - */ -GstFlowReturn -gst_harness_push_from_src (GstHarness * h) -{ - GstBuffer *buf; - gboolean crank; - - g_assert (h->src_harness); - - /* FIXME: this *is* the right time to start the src, - but maybe a flag so we don't keep telling it to play? */ - gst_harness_play (h->src_harness); - - if (h->src_harness->priv->has_clock_wait) { - crank = gst_harness_crank_single_clock_wait (h->src_harness); - g_assert (crank); - } - - buf = gst_harness_pull (h->src_harness); - g_assert (buf != NULL); - return gst_harness_push (h, buf); -} - -/** - * gst_harness_src_crank_and_push_many: - * @h: a #GstHarness - * @cranks: a #gint with the number of calls to gst_harness_crank_single_clock_wait - * @pushes: a #gint with the number of calls to gst_harness_push - * - * Transfer data from the src-#GstHarness to the main-#GstHarness. Similar to - * gst_harness_push_from_src, this variant allows you to specify how many cranks - * and how many pushes to perform. This can be useful for both moving a lot - * of data at the same time, as well as cases when one crank does not equal one - * buffer to push and v.v. - * - * MT safe. - * - * Returns: a #GstFlowReturn with the result of the push - * - * Since: 1.6 - */ -GstFlowReturn -gst_harness_src_crank_and_push_many (GstHarness * h, gint cranks, gint pushes) -{ - GstFlowReturn ret = GST_FLOW_OK; - gboolean crank; - int i; - - g_assert (h->src_harness); - gst_harness_play (h->src_harness); - - for (i = 0; i < cranks; i++) { - crank = gst_harness_crank_single_clock_wait (h->src_harness); - g_assert (crank); - } - - for (i = 0; i < pushes; i++) { - GstBuffer *buf; - buf = gst_harness_pull (h->src_harness); - g_assert (buf != NULL); - ret = gst_harness_push (h, buf); - if (ret != GST_FLOW_OK) - break; - } - - return ret; -} - -/** - * gst_harness_src_push_event: - * @h: a #GstHarness - * - * Similar to what gst_harness_src_push does with #GstBuffers, this transfers - * a #GstEvent from the src-#GstHarness to the main-#GstHarness. Note that - * some #GstEvents are being transferred automagically. Look at sink_forward_pad - * for details. - * - * MT safe. - * - * Returns: a #gboolean with the result of the push - * - * Since: 1.6 - */ -gboolean -gst_harness_src_push_event (GstHarness * h) -{ - return gst_harness_push_event (h, gst_harness_pull_event (h->src_harness)); -} - - -static gboolean -forward_sticky_events (GstPad * pad, GstEvent ** ev, gpointer user_data) -{ - GstPad *fwdpad = user_data; - return gst_pad_push_event (fwdpad, gst_event_ref (*ev)); -} - -/** - * gst_harness_add_sink_harness: - * @h: a #GstHarness - * @sink_harness: (transfer full): a #GstHarness to be added as a sink-harness. - * - * Similar to gst_harness_add_src, this allows you to send the data coming out - * of your harnessed #GstElement to a sink-element, allowing to test different - * responses the element output might create in sink elements. An example might - * be an existing sink providing some analytical data on the input it receives that - * can be useful to your testing. If the goal is to test a sink-element itself, - * this is better achieved using gst_harness_new directly on the sink. - * - * If a sink-harness already exists it will be replaced. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_add_sink_harness (GstHarness * h, GstHarness * sink_harness) -{ - GstHarnessPrivate *priv; - GstPad *fwdpad; - - HARNESS_LOCK (h); - priv = h->priv; - - if (h->sink_harness) { - gst_harness_set_forward_pad (h, NULL); - gst_harness_teardown (h->sink_harness); - } - h->sink_harness = sink_harness; - - fwdpad = h->sink_harness->srcpad; - if (fwdpad) - gst_object_ref (fwdpad); - - if (priv->forwarding && h->sinkpad && fwdpad) { - HARNESS_UNLOCK (h); - gst_pad_sticky_events_foreach (h->sinkpad, forward_sticky_events, fwdpad); - HARNESS_LOCK (h); - } - - gst_harness_set_forward_pad (h, fwdpad); - if (fwdpad) - gst_object_unref (fwdpad); - - gst_harness_set_forwarding (h->sink_harness, priv->forwarding); - - HARNESS_UNLOCK (h); -} - -/** - * gst_harness_add_sink: - * @h: a #GstHarness - * @sink_element_name: a #gchar with the name of a #GstElement - * - * Similar to gst_harness_add_sink_harness, this is a convenience to - * directly create a sink-harness using the @sink_element_name name specified. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_add_sink (GstHarness * h, const gchar * sink_element_name) -{ - GstHarness *sink_harness = gst_harness_new (sink_element_name); - gst_harness_add_sink_harness (h, sink_harness); -} - -/** - * gst_harness_add_sink_parse: - * @h: a #GstHarness - * @launchline: a #gchar with the name of a #GstElement - * - * Similar to gst_harness_add_sink, this allows you to specify a launch-line - * instead of just an element name. See gst_harness_add_src_parse for details. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_add_sink_parse (GstHarness * h, const gchar * launchline) -{ - GstHarness *sink_harness = gst_harness_new_parse (launchline); - gst_harness_add_sink_harness (h, sink_harness); -} - -/** - * gst_harness_push_to_sink: - * @h: a #GstHarness - * - * Transfer one #GstBuffer from the main-#GstHarness to the sink-#GstHarness. - * See gst_harness_push_from_src for details. - * - * MT safe. - * - * Returns: a #GstFlowReturn with the result of the push - * - * Since: 1.6 - */ -GstFlowReturn -gst_harness_push_to_sink (GstHarness * h) -{ - GstBuffer *buf; - g_assert (h->sink_harness); - buf = gst_harness_pull (h); - g_assert (buf != NULL); - return gst_harness_push (h->sink_harness, buf); -} - -/** - * gst_harness_sink_push_many: - * @h: a #GstHarness - * @pushes: a #gint with the number of calls to gst_harness_push_to_sink - * - * Convenience that calls gst_harness_push_to_sink @pushes number of times. - * Will abort the pushing if any one push fails. - * - * MT safe. - * - * Returns: a #GstFlowReturn with the result of the push - * - * Since: 1.6 - */ -GstFlowReturn -gst_harness_sink_push_many (GstHarness * h, gint pushes) -{ - GstFlowReturn ret = GST_FLOW_OK; - int i; - g_assert (h->sink_harness); - for (i = 0; i < pushes; i++) { - ret = gst_harness_push_to_sink (h); - if (ret != GST_FLOW_OK) - break; - } - return ret; -} - -/** - * gst_harness_find_element: - * @h: a #GstHarness - * @element_name: a #gchar with a #GstElementFactory name - * - * Most useful in conjunction with gst_harness_new_parse, this will scan the - * #GstElements inside the #GstHarness, and check if any of them matches - * @element_name. Typical usecase being that you need to access one of the - * harnessed elements for properties and/or signals. - * - * MT safe. - * - * Returns: (transfer full) (allow-none): a #GstElement or %NULL if not found - * - * Since: 1.6 - */ -GstElement * -gst_harness_find_element (GstHarness * h, const gchar * element_name) -{ - gboolean done = FALSE; - GstIterator *iter; - GValue data = G_VALUE_INIT; - - if (!GST_IS_BIN (h->element)) { - GstPluginFeature *feature; - - g_return_val_if_fail (GST_IS_ELEMENT (h->element), NULL); - - feature = GST_PLUGIN_FEATURE (gst_element_get_factory (h->element)); - if (!strcmp (element_name, gst_plugin_feature_get_name (feature))) - return gst_object_ref (h->element); - - return NULL; - } - - iter = gst_bin_iterate_elements (GST_BIN (h->element)); - done = FALSE; - - while (!done) { - switch (gst_iterator_next (iter, &data)) { - case GST_ITERATOR_OK: - { - GstElement *element = g_value_get_object (&data); - GstPluginFeature *feature = - GST_PLUGIN_FEATURE (gst_element_get_factory (element)); - if (!strcmp (element_name, gst_plugin_feature_get_name (feature))) { - gst_iterator_free (iter); - return element; - } - g_value_reset (&data); - break; - } - case GST_ITERATOR_RESYNC: - gst_iterator_resync (iter); - break; - case GST_ITERATOR_ERROR: - case GST_ITERATOR_DONE: - done = TRUE; - break; - } - } - gst_iterator_free (iter); - - return NULL; -} - -/** - * gst_harness_set: - * @h: a #GstHarness - * @element_name: a #gchar with a #GstElementFactory name - * @first_property_name: a #gchar with the first property name - * @...: value for the first property, followed optionally by more - * name/value pairs, followed by %NULL - * - * A convenience function to allows you to call g_object_set on a #GstElement - * that are residing inside the #GstHarness, by using normal g_object_set - * syntax. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_set (GstHarness * h, - const gchar * element_name, const gchar * first_property_name, ...) -{ - va_list var_args; - GstElement *element = gst_harness_find_element (h, element_name); - va_start (var_args, first_property_name); - g_object_set_valist (G_OBJECT (element), first_property_name, var_args); - va_end (var_args); - gst_object_unref (element); -} - -/** - * gst_harness_get: - * @h: a #GstHarness - * @element_name: a #gchar with a #GstElementFactory name - * @first_property_name: a #gchar with the first property name - * @...: return location for the first property, followed optionally by more - * name/return location pairs, followed by %NULL - * - * A convenience function to allows you to call g_object_get on a #GstElement - * that are residing inside the #GstHarness, by using normal g_object_get - * syntax. - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_get (GstHarness * h, - const gchar * element_name, const gchar * first_property_name, ...) -{ - va_list var_args; - GstElement *element = gst_harness_find_element (h, element_name); - va_start (var_args, first_property_name); - g_object_get_valist (G_OBJECT (element), first_property_name, var_args); - va_end (var_args); - gst_object_unref (element); -} - -/** - * gst_harness_add_probe: - * @h: a #GstHarness - * @element_name: a #gchar with a #GstElementFactory name - * @pad_name: a #gchar with the name of the pad to attach the probe to - * @mask: a #GstPadProbeType (see gst_pad_add_probe) - * @callback: a #GstPadProbeCallback (see gst_pad_add_probe) - * @user_data: a #gpointer (see gst_pad_add_probe) - * @destroy_data: a #GDestroyNotify (see gst_pad_add_probe) - * - * A convenience function to allows you to call gst_pad_add_probe on a - * #GstPad of a #GstElement that are residing inside the #GstHarness, - * by using normal gst_pad_add_probe syntax - * - * MT safe. - * - * Since: 1.6 - */ -void -gst_harness_add_probe (GstHarness * h, - const gchar * element_name, const gchar * pad_name, GstPadProbeType mask, - GstPadProbeCallback callback, gpointer user_data, - GDestroyNotify destroy_data) -{ - GstElement *element = gst_harness_find_element (h, element_name); - GstPad *pad = gst_element_get_static_pad (element, pad_name); - gst_pad_add_probe (pad, mask, callback, user_data, destroy_data); - gst_object_unref (pad); - gst_object_unref (element); -} - -/******************************************************************************/ -/* STRESS */ -/******************************************************************************/ -struct _GstHarnessThread -{ - GstHarness *h; - GThread *thread; - gboolean running; - - gulong sleep; - - GDestroyNotify freefunc; -}; - -typedef struct -{ - GstHarnessThread t; - - GFunc init; - GFunc callback; - gpointer data; -} GstHarnessCustomThread; - -typedef struct -{ - GstHarnessThread t; - - GstCaps *caps; - GstSegment segment; - GstHarnessPrepareBufferFunc func; - gpointer data; - GDestroyNotify notify; -} GstHarnessPushBufferThread; - -typedef struct -{ - GstHarnessThread t; - - GstHarnessPrepareEventFunc func; - gpointer data; - GDestroyNotify notify; -} GstHarnessPushEventThread; - -typedef struct -{ - GstHarnessThread t; - - gchar *name; - GValue value; -} GstHarnessPropThread; - -typedef struct -{ - GstHarnessThread t; - - GstPadTemplate *templ; - gchar *name; - GstCaps *caps; - gboolean release; - - GSList *pads; -} GstHarnessReqPadThread; - -static void -gst_harness_thread_init (GstHarnessThread * t, GDestroyNotify freefunc, - GstHarness * h, gulong sleep) -{ - t->freefunc = freefunc; - t->h = h; - t->sleep = sleep; - - g_ptr_array_add (h->priv->stress, t); -} - -static void -gst_harness_thread_free (GstHarnessThread * t) -{ - g_slice_free (GstHarnessThread, t); -} - -static void -gst_harness_custom_thread_free (GstHarnessCustomThread * t) -{ - g_slice_free (GstHarnessCustomThread, t); -} - -static void -gst_harness_push_buffer_thread_free (GstHarnessPushBufferThread * t) -{ - if (t != NULL) { - gst_caps_replace (&t->caps, NULL); - if (t->notify != NULL) - t->notify (t->data); - g_slice_free (GstHarnessPushBufferThread, t); - } -} - -static void -gst_harness_push_event_thread_free (GstHarnessPushEventThread * t) -{ - if (t != NULL) { - if (t->notify != NULL) - t->notify (t->data); - g_slice_free (GstHarnessPushEventThread, t); - } -} - -static void -gst_harness_property_thread_free (GstHarnessPropThread * t) -{ - if (t != NULL) { - g_free (t->name); - g_value_unset (&t->value); - g_slice_free (GstHarnessPropThread, t); - } -} - -static void -gst_harness_requestpad_release (GstPad * pad, GstElement * element) -{ - gst_element_release_request_pad (element, pad); - gst_object_unref (pad); -} - -static void -gst_harness_requestpad_release_pads (GstHarnessReqPadThread * rpt) -{ - g_slist_foreach (rpt->pads, (GFunc) gst_harness_requestpad_release, - rpt->t.h->element); - g_slist_free (rpt->pads); - rpt->pads = NULL; -} - -static void -gst_harness_requestpad_thread_free (GstHarnessReqPadThread * t) -{ - if (t != NULL) { - gst_object_replace ((GstObject **) & t->templ, NULL); - g_free (t->name); - gst_caps_replace (&t->caps, NULL); - - gst_harness_requestpad_release_pads (t); - g_slice_free (GstHarnessReqPadThread, t); - } -} - -#define GST_HARNESS_THREAD_START(ID, t) \ - (((GstHarnessThread *)t)->running = TRUE, \ - ((GstHarnessThread *)t)->thread = g_thread_new ( \ - "gst-harness-stress-"G_STRINGIFY(ID), \ - (GThreadFunc)gst_harness_stress_##ID##_func, t)) -#define GST_HARNESS_THREAD_END(t) \ - (t->running = FALSE, \ - GPOINTER_TO_UINT (g_thread_join (t->thread))) - -static void -gst_harness_stress_free (GstHarnessThread * t) -{ - if (t != NULL && t->freefunc != NULL) - t->freefunc (t); -} - -static gpointer -gst_harness_stress_custom_func (GstHarnessThread * t) -{ - GstHarnessCustomThread *ct = (GstHarnessCustomThread *) t; - guint count = 0; - - if (ct->init != NULL) - ct->init (ct, ct->data); - - while (t->running) { - ct->callback (ct, ct->data); - - count++; - g_usleep (t->sleep); - } - return GUINT_TO_POINTER (count); -} - - -static gpointer -gst_harness_stress_statechange_func (GstHarnessThread * t) -{ - guint count = 0; - - while (t->running) { - GstClock *clock = gst_element_get_clock (t->h->element); - GstIterator *it; - gboolean done = FALSE; - gboolean change; - - change = gst_element_set_state (t->h->element, GST_STATE_NULL); - g_assert (change == GST_STATE_CHANGE_SUCCESS); - g_thread_yield (); - - it = gst_element_iterate_sink_pads (t->h->element); - while (!done) { - GValue item = G_VALUE_INIT; - switch (gst_iterator_next (it, &item)) { - case GST_ITERATOR_OK: - { - GstPad *sinkpad = g_value_get_object (&item); - GstPad *srcpad = gst_pad_get_peer (sinkpad); - if (srcpad != NULL) { - gst_pad_unlink (srcpad, sinkpad); - gst_pad_link (srcpad, sinkpad); - gst_object_unref (srcpad); - } - g_value_reset (&item); - break; - } - case GST_ITERATOR_RESYNC: - gst_iterator_resync (it); - break; - case GST_ITERATOR_ERROR: - g_assert_not_reached (); - case GST_ITERATOR_DONE: - done = TRUE; - break; - } - g_value_unset (&item); - } - gst_iterator_free (it); - - if (clock != NULL) { - gst_element_set_clock (t->h->element, clock); - gst_object_unref (clock); - } - change = gst_element_set_state (t->h->element, GST_STATE_PLAYING); - g_assert (change == GST_STATE_CHANGE_SUCCESS); - - count++; - g_usleep (t->sleep); - } - return GUINT_TO_POINTER (count); -} - -static gpointer -gst_harness_stress_buffer_func (GstHarnessThread * t) -{ - GstHarnessPushBufferThread *pt = (GstHarnessPushBufferThread *) t; - guint count = 0; - gchar *sid; - gboolean handled; - - /* Push stream start, caps and segment events */ - sid = g_strdup_printf ("%s-%p", GST_OBJECT_NAME (t->h->element), t->h); - handled = gst_pad_push_event (t->h->srcpad, gst_event_new_stream_start (sid)); - g_assert (handled); - g_free (sid); - handled = gst_pad_push_event (t->h->srcpad, gst_event_new_caps (pt->caps)); - g_assert (handled); - handled = gst_pad_push_event (t->h->srcpad, - gst_event_new_segment (&pt->segment)); - g_assert (handled); - - while (t->running) { - gst_harness_push (t->h, pt->func (t->h, pt->data)); - - count++; - g_usleep (t->sleep); - } - return GUINT_TO_POINTER (count); -} - -static gpointer -gst_harness_stress_event_func (GstHarnessThread * t) -{ - GstHarnessPushEventThread *pet = (GstHarnessPushEventThread *) t; - guint count = 0; - - while (t->running) { - gst_harness_push_event (t->h, pet->func (t->h, pet->data)); - - count++; - g_usleep (t->sleep); - } - return GUINT_TO_POINTER (count); -} - -static gpointer -gst_harness_stress_upstream_event_func (GstHarnessThread * t) -{ - GstHarnessPushEventThread *pet = (GstHarnessPushEventThread *) t; - guint count = 0; - - while (t->running) { - gst_harness_push_upstream_event (t->h, pet->func (t->h, pet->data)); - - count++; - g_usleep (t->sleep); - } - return GUINT_TO_POINTER (count); -} - -static gpointer -gst_harness_stress_property_func (GstHarnessThread * t) -{ - GstHarnessPropThread *pt = (GstHarnessPropThread *) t; - guint count = 0; - - while (t->running) { - GValue value = G_VALUE_INIT; - - g_object_set_property (G_OBJECT (t->h->element), pt->name, &pt->value); - - g_value_init (&value, G_VALUE_TYPE (&pt->value)); - g_object_get_property (G_OBJECT (t->h->element), pt->name, &value); - g_value_reset (&value); - - count++; - g_usleep (t->sleep); - } - return GUINT_TO_POINTER (count); -} - -static gpointer -gst_harness_stress_requestpad_func (GstHarnessThread * t) -{ - GstHarnessReqPadThread *rpt = (GstHarnessReqPadThread *) t; - guint count = 0; - - while (t->running) { - GstPad *reqpad; - - if (rpt->release) - gst_harness_requestpad_release_pads (rpt); - - g_thread_yield (); - - reqpad = gst_element_request_pad (t->h->element, - rpt->templ, rpt->name, rpt->caps); - - g_assert (reqpad != NULL); - - rpt->pads = g_slist_prepend (rpt->pads, reqpad); - - count++; - g_usleep (t->sleep); - } - return GUINT_TO_POINTER (count); -} - -/** - * gst_harness_stress_thread_stop: - * @t: a #GstHarnessThread - * - * Stop the running #GstHarnessThread - * - * MT safe. - * - * Since: 1.6 - */ -guint -gst_harness_stress_thread_stop (GstHarnessThread * t) -{ - guint ret; - - g_return_val_if_fail (t != NULL, 0); - - ret = GST_HARNESS_THREAD_END (t); - g_ptr_array_remove (t->h->priv->stress, t); - return ret; -} - -/** - * gst_harness_stress_custom_start: (skip) - * @h: a #GstHarness - * @init: (allow-none): a #GFunc that is called initially and only once - * @callback: a #GFunc that is called as often as possible - * @data: a #gpointer with custom data to pass to the @callback function - * @sleep: a #gulong specifying how long to sleep in (microseconds) for - * each call to the @callback - * - * Start a custom stress-thread that will call your @callback for every - * iteration allowing you to do something nasty. - * - * MT safe. - * - * Returns: a #GstHarnessThread - * - * Since: 1.6 - */ -GstHarnessThread * -gst_harness_stress_custom_start (GstHarness * h, - GFunc init, GFunc callback, gpointer data, gulong sleep) -{ - GstHarnessCustomThread *t = g_slice_new0 (GstHarnessCustomThread); - gst_harness_thread_init (&t->t, - (GDestroyNotify) gst_harness_custom_thread_free, h, sleep); - - t->init = init; - t->callback = callback; - t->data = data; - - GST_HARNESS_THREAD_START (custom, t); - return &t->t; -} - -/** - * gst_harness_stress_statechange_start_full: (skip) - * @h: a #GstHarness - * @sleep: a #gulong specifying how long to sleep in (microseconds) for - * each state-change - * - * Change the state of your harnessed #GstElement from NULL to PLAYING and - * back again, only pausing for @sleep microseconds every time. - * - * MT safe. - * - * Returns: a #GstHarnessThread - * - * Since: 1.6 - */ -GstHarnessThread * -gst_harness_stress_statechange_start_full (GstHarness * h, gulong sleep) -{ - GstHarnessThread *t = g_slice_new0 (GstHarnessThread); - gst_harness_thread_init (t, - (GDestroyNotify) gst_harness_thread_free, h, sleep); - GST_HARNESS_THREAD_START (statechange, t); - return t; -} - -static GstBuffer * -gst_harness_ref_buffer (GstHarness * h, gpointer data) -{ - (void) h; - return gst_buffer_ref (GST_BUFFER_CAST (data)); -} - -static GstEvent * -gst_harness_ref_event (GstHarness * h, gpointer data) -{ - (void) h; - return gst_event_ref (GST_EVENT_CAST (data)); -} - -/** - * gst_harness_stress_push_buffer_start_full: (skip) - * @h: a #GstHarness - * @caps: a #GstCaps for the #GstBuffer - * @segment: a #GstSegment - * @buf: a #GstBuffer to push - * @sleep: a #gulong specifying how long to sleep in (microseconds) for - * each call to gst_pad_push - * - * Push a #GstBuffer in intervals of @sleep microseconds. - * - * MT safe. - * - * Returns: a #GstHarnessThread - * - * Since: 1.6 - */ -GstHarnessThread * -gst_harness_stress_push_buffer_start_full (GstHarness * h, - GstCaps * caps, const GstSegment * segment, GstBuffer * buf, gulong sleep) -{ - return gst_harness_stress_push_buffer_with_cb_start_full (h, caps, segment, - gst_harness_ref_buffer, gst_buffer_ref (buf), - (GDestroyNotify) gst_buffer_unref, sleep); -} - -/** - * gst_harness_stress_push_buffer_with_cb_start_full: (skip) - * @h: a #GstHarness - * @caps: a #GstCaps for the #GstBuffer - * @segment: a #GstSegment - * @func: a #GstHarnessPrepareBufferFunc function called before every iteration - * to prepare / create a #GstBuffer for pushing - * @data: a #gpointer with data to the #GstHarnessPrepareBufferFunc function - * @notify: a #GDestroyNotify that is called when thread is stopped - * @sleep: a #gulong specifying how long to sleep in (microseconds) for - * each call to gst_pad_push - * - * Push a #GstBuffer returned by @func in intervals of @sleep microseconds. - * - * MT safe. - * - * Returns: a #GstHarnessThread - * - * Since: 1.6 - */ -GstHarnessThread * -gst_harness_stress_push_buffer_with_cb_start_full (GstHarness * h, - GstCaps * caps, const GstSegment * segment, - GstHarnessPrepareBufferFunc func, gpointer data, GDestroyNotify notify, - gulong sleep) -{ - GstHarnessPushBufferThread *t = g_slice_new0 (GstHarnessPushBufferThread); - gst_harness_thread_init (&t->t, - (GDestroyNotify) gst_harness_push_buffer_thread_free, h, sleep); - - gst_caps_replace (&t->caps, caps); - t->segment = *segment; - t->func = func; - t->data = data; - t->notify = notify; - - GST_HARNESS_THREAD_START (buffer, t); - return &t->t; -} - -/** - * gst_harness_stress_push_event_start_full: (skip) - * @h: a #GstHarness - * @event: a #GstEvent to push - * @sleep: a #gulong specifying how long to sleep in (microseconds) for - * each gst_event_push with @event - * - * Push the @event onto the harnessed #GstElement sinkpad in intervals of - * @sleep microseconds - * - * MT safe. - * - * Returns: a #GstHarnessThread - * - * Since: 1.6 - */ -GstHarnessThread * -gst_harness_stress_push_event_start_full (GstHarness * h, - GstEvent * event, gulong sleep) -{ - return gst_harness_stress_push_event_with_cb_start_full (h, - gst_harness_ref_event, gst_event_ref (event), - (GDestroyNotify) gst_event_unref, sleep); -} - -/** - * gst_harness_stress_push_event_with_cb_start_full: (skip) - * @h: a #GstHarness - * @func: a #GstHarnessPrepareEventFunc function called before every iteration - * to prepare / create a #GstEvent for pushing - * @data: a #gpointer with data to the #GstHarnessPrepareEventFunc function - * @notify: a #GDestroyNotify that is called when thread is stopped - * @sleep: a #gulong specifying how long to sleep in (microseconds) for - * each call to gst_pad_push - * - * Push a #GstEvent returned by @func onto the harnessed #GstElement sinkpad - * in intervals of @sleep microseconds. - * - * MT safe. - * - * Returns: a #GstHarnessThread - * - * Since: 1.8 - */ -GstHarnessThread * -gst_harness_stress_push_event_with_cb_start_full (GstHarness * h, - GstHarnessPrepareEventFunc func, gpointer data, GDestroyNotify notify, - gulong sleep) -{ - GstHarnessPushEventThread *t = g_slice_new0 (GstHarnessPushEventThread); - gst_harness_thread_init (&t->t, - (GDestroyNotify) gst_harness_push_event_thread_free, h, sleep); - - t->func = func; - t->data = data; - t->notify = notify; - - GST_HARNESS_THREAD_START (event, t); - return &t->t; -} - -/** - * gst_harness_stress_push_upstream_event_start_full: (skip) - * @h: a #GstHarness - * @event: a #GstEvent to push - * @sleep: a #gulong specifying how long to sleep in (microseconds) for - * each gst_event_push with @event - * - * Push the @event onto the harnessed #GstElement srcpad in intervals of - * @sleep microseconds. - * - * MT safe. - * - * Returns: a #GstHarnessThread - * - * Since: 1.6 - */ -GstHarnessThread * -gst_harness_stress_push_upstream_event_start_full (GstHarness * h, - GstEvent * event, gulong sleep) -{ - return gst_harness_stress_push_upstream_event_with_cb_start_full (h, - gst_harness_ref_event, gst_event_ref (event), - (GDestroyNotify) gst_event_unref, sleep); -} - -/** - * gst_harness_stress_push_upstream_event_with_cb_start_full: (skip) - * @h: a #GstHarness - * @func: a #GstHarnessPrepareEventFunc function called before every iteration - * to prepare / create a #GstEvent for pushing - * @data: a #gpointer with data to the #GstHarnessPrepareEventFunc function - * @notify: a #GDestroyNotify that is called when thread is stopped - * @sleep: a #gulong specifying how long to sleep in (microseconds) for - * each call to gst_pad_push - * - * Push a #GstEvent returned by @func onto the harnessed #GstElement srcpad - * in intervals of @sleep microseconds. - * - * MT safe. - * - * Returns: a #GstHarnessThread - * - * Since: 1.8 - */ -GstHarnessThread * -gst_harness_stress_push_upstream_event_with_cb_start_full (GstHarness * h, - GstHarnessPrepareEventFunc func, gpointer data, GDestroyNotify notify, - gulong sleep) -{ - GstHarnessPushEventThread *t = g_slice_new0 (GstHarnessPushEventThread); - gst_harness_thread_init (&t->t, - (GDestroyNotify) gst_harness_push_event_thread_free, h, sleep); - - t->func = func; - t->data = data; - t->notify = notify; - - GST_HARNESS_THREAD_START (upstream_event, t); - return &t->t; -} - -/** - * gst_harness_stress_property_start_full: (skip) - * @h: a #GstHarness - * @name: a #gchar specifying a property name - * @value: a #GValue to set the property to - * @sleep: a #gulong specifying how long to sleep in (microseconds) for - * each g_object_set with @name and @value - * - * Call g_object_set with @name and @value in intervals of @sleep microseconds - * - * MT safe. - * - * Returns: a #GstHarnessThread - * - * Since: 1.6 - */ -GstHarnessThread * -gst_harness_stress_property_start_full (GstHarness * h, - const gchar * name, const GValue * value, gulong sleep) -{ - GstHarnessPropThread *t = g_slice_new0 (GstHarnessPropThread); - gst_harness_thread_init (&t->t, - (GDestroyNotify) gst_harness_property_thread_free, h, sleep); - - t->name = g_strdup (name); - g_value_init (&t->value, G_VALUE_TYPE (value)); - g_value_copy (value, &t->value); - - GST_HARNESS_THREAD_START (property, t); - return &t->t; -} - -/** - * gst_harness_stress_requestpad_start_full: (skip) - * @h: a #GstHarness - * @templ: a #GstPadTemplate - * @name: a #gchar - * @caps: a #GstCaps - * @release: a #gboolean - * @sleep: a #gulong specifying how long to sleep in (microseconds) for - * each gst_element_request_pad - * - * Call gst_element_request_pad in intervals of @sleep microseconds - * - * MT safe. - * - * Returns: a #GstHarnessThread - * - * Since: 1.6 - */ -GstHarnessThread * -gst_harness_stress_requestpad_start_full (GstHarness * h, - GstPadTemplate * templ, const gchar * name, GstCaps * caps, - gboolean release, gulong sleep) -{ - GstHarnessReqPadThread *t = g_slice_new0 (GstHarnessReqPadThread); - gst_harness_thread_init (&t->t, - (GDestroyNotify) gst_harness_requestpad_thread_free, h, sleep); - - t->templ = gst_object_ref (templ); - t->name = g_strdup (name); - gst_caps_replace (&t->caps, caps); - t->release = release; - - GST_HARNESS_THREAD_START (requestpad, t); - return &t->t; -} diff --git a/libs/gst/check/gstharness.h b/libs/gst/check/gstharness.h deleted file mode 100644 index 160fdb01d8..0000000000 --- a/libs/gst/check/gstharness.h +++ /dev/null @@ -1,474 +0,0 @@ -/* GstHarness - A test-harness for GStreamer testing - * - * Copyright (C) 2012-2015 Pexip <pexip.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __GST_HARNESS_H__ -#define __GST_HARNESS_H__ - -#include <gst/gst.h> -#include <gst/check/gsttestclock.h> -#include <gst/check/check-prelude.h> - -G_BEGIN_DECLS - -/** - * GstHarnessThread: - * - * Opaque handle representing a GstHarness stress testing thread. - * - * Since: 1.6 - */ -typedef struct _GstHarnessThread GstHarnessThread; - -typedef struct _GstHarness GstHarness; -typedef struct _GstHarnessPrivate GstHarnessPrivate; - -/** - * GstHarness: - * @element: the element inside the harness - * @srcpad: the internal harness source pad - * @sinkpad: the internal harness sink pad - * @src_harness: the source (input) harness (if any) - * @sink_harness: the sink (output) harness (if any) - * - * Since: 1.6 - */ -struct _GstHarness { - GstElement * element; - - GstPad * srcpad; - GstPad * sinkpad; - - GstHarness * src_harness; - GstHarness * sink_harness; - - /*< private >*/ - GstHarnessPrivate * priv; -}; - -/* Harness creation */ - -GST_CHECK_API -GstHarness * gst_harness_new_empty (void); - -GST_CHECK_API -void gst_harness_add_element_full (GstHarness * h, - GstElement * element, - GstStaticPadTemplate * hsrc, - const gchar * element_sinkpad_name, - GstStaticPadTemplate * hsink, - const gchar * element_srcpad_name); - -GST_CHECK_API -GstHarness * gst_harness_new_full (GstElement * element, - GstStaticPadTemplate * hsrc, - const gchar * element_sinkpad_name, - GstStaticPadTemplate * hsink, - const gchar * element_srcpad_name); - -GST_CHECK_API -GstHarness * gst_harness_new_with_element (GstElement * element, - const gchar * element_sinkpad_name, - const gchar * element_srcpad_name); - -GST_CHECK_API -GstHarness * gst_harness_new_with_padnames (const gchar * element_name, - const gchar * element_sinkpad_name, - const gchar * element_srcpad_name); - -GST_CHECK_API -GstHarness * gst_harness_new_with_templates (const gchar * element_name, - GstStaticPadTemplate * hsrc, - GstStaticPadTemplate * hsink); - -GST_CHECK_API -GstHarness * gst_harness_new (const gchar * element_name); - -GST_CHECK_API -GstHarness * gst_harness_new_parse (const gchar * launchline); - -GST_CHECK_API -void gst_harness_add_parse (GstHarness * h, const gchar * launchline); - -GST_CHECK_API -void gst_harness_teardown (GstHarness * h); - -GST_CHECK_API -void gst_harness_add_element_src_pad (GstHarness * h, GstPad * srcpad); - -GST_CHECK_API -void gst_harness_add_element_sink_pad (GstHarness * h, GstPad * sinkpad); - -/* Caps Functions */ - -GST_CHECK_API -void gst_harness_set_src_caps (GstHarness * h, GstCaps * caps); - -GST_CHECK_API -void gst_harness_set_sink_caps (GstHarness * h, GstCaps * caps); - -GST_CHECK_API -void gst_harness_set_caps (GstHarness * h, GstCaps * in, GstCaps * out); - -GST_CHECK_API -void gst_harness_set_src_caps_str (GstHarness * h, const gchar * str); - -GST_CHECK_API -void gst_harness_set_sink_caps_str (GstHarness * h, const gchar * str); - -GST_CHECK_API -void gst_harness_set_caps_str (GstHarness * h, - const gchar * in, - const gchar * out); - -/* Clock Functions */ - -GST_CHECK_API -void gst_harness_use_systemclock (GstHarness * h); - -GST_CHECK_API -void gst_harness_use_testclock (GstHarness * h); - -GST_CHECK_API -GstTestClock * gst_harness_get_testclock (GstHarness * h); - -GST_CHECK_API -gboolean gst_harness_set_time (GstHarness * h, GstClockTime time); - -GST_CHECK_API -gboolean gst_harness_wait_for_clock_id_waits (GstHarness * h, - guint waits, - guint timeout); - -GST_CHECK_API -gboolean gst_harness_crank_single_clock_wait (GstHarness * h); - -GST_CHECK_API -gboolean gst_harness_crank_multiple_clock_waits (GstHarness * h, - guint waits); - -/* misc */ - -GST_CHECK_API -void gst_harness_play (GstHarness * h); - -GST_CHECK_API -void gst_harness_set_blocking_push_mode (GstHarness * h); - -GST_CHECK_API -void gst_harness_set_forwarding (GstHarness * h, gboolean forwarding); - -/* buffers */ - -GST_CHECK_API -GstBuffer * gst_harness_create_buffer (GstHarness * h, gsize size); - -GST_CHECK_API -GstFlowReturn gst_harness_push (GstHarness * h, GstBuffer * buffer); - -GST_CHECK_API -GstBuffer * gst_harness_pull (GstHarness * h); - -GST_CHECK_API -GstBuffer * gst_harness_try_pull (GstHarness * h); - -GST_CHECK_API -gboolean gst_harness_pull_until_eos (GstHarness * h, GstBuffer ** buf); - -GST_CHECK_API -GstBuffer * gst_harness_push_and_pull (GstHarness * h, GstBuffer * buffer); - -GST_CHECK_API -guint gst_harness_buffers_received (GstHarness * h); - -GST_CHECK_API -guint gst_harness_buffers_in_queue (GstHarness * h); - -GST_CHECK_API -void gst_harness_set_drop_buffers (GstHarness * h, gboolean drop_buffers); - -GST_CHECK_API -void gst_harness_dump_to_file (GstHarness * h, const gchar * filename); - -GST_CHECK_API -guint8 * gst_harness_take_all_data (GstHarness * h, gsize * size); - -GST_CHECK_API -GstBuffer * gst_harness_take_all_data_as_buffer (GstHarness * h); - -GST_CHECK_API -GBytes * gst_harness_take_all_data_as_bytes (GstHarness * h); - -GST_CHECK_API -GstClockTime gst_harness_get_last_pushed_timestamp (GstHarness * h); - -/* downstream events */ - -GST_CHECK_API -gboolean gst_harness_push_event (GstHarness * h, GstEvent * event); - -GST_CHECK_API -GstEvent * gst_harness_pull_event (GstHarness * h); - -GST_CHECK_API -GstEvent * gst_harness_try_pull_event (GstHarness * h); - -GST_CHECK_API -guint gst_harness_events_received (GstHarness * h); - -GST_CHECK_API -guint gst_harness_events_in_queue (GstHarness * h); - -/* upstream events */ - -GST_CHECK_API -gboolean gst_harness_push_upstream_event (GstHarness * h, GstEvent * event); - -GST_CHECK_API -GstEvent * gst_harness_pull_upstream_event (GstHarness * h); - -GST_CHECK_API -GstEvent * gst_harness_try_pull_upstream_event (GstHarness * h); - -GST_CHECK_API -guint gst_harness_upstream_events_received (GstHarness * h); - -GST_CHECK_API -guint gst_harness_upstream_events_in_queue (GstHarness * h); - -/* latency */ - -GST_CHECK_API -GstClockTime gst_harness_query_latency (GstHarness * h); - -GST_CHECK_API -void gst_harness_set_upstream_latency (GstHarness * h, GstClockTime latency); - -GST_CHECK_API -void gst_harness_set_live (GstHarness * h, gboolean is_live); - -/* allocation query parameters */ - -GST_CHECK_API -void gst_harness_set_propose_allocator (GstHarness * h, - GstAllocator * allocator, - const GstAllocationParams * params); - -GST_CHECK_API -void gst_harness_get_allocator (GstHarness * h, - GstAllocator ** allocator, - GstAllocationParams * params); - -GST_CHECK_API -void gst_harness_add_propose_allocation_meta (GstHarness * h, - GType api, - const GstStructure * params); - -/* src-harness */ - -GST_CHECK_API -void gst_harness_add_src_harness (GstHarness * h, - GstHarness * src_harness, - gboolean has_clock_wait); - -GST_CHECK_API -void gst_harness_add_src (GstHarness * h, - const gchar * src_element_name, - gboolean has_clock_wait); - -GST_CHECK_API -void gst_harness_add_src_parse (GstHarness * h, - const gchar * launchline, - gboolean has_clock_wait); - -GST_CHECK_API -GstFlowReturn gst_harness_push_from_src (GstHarness * h); - -GST_CHECK_API -GstFlowReturn gst_harness_src_crank_and_push_many (GstHarness * h, - gint cranks, - gint pushes); - -GST_CHECK_API -gboolean gst_harness_src_push_event (GstHarness * h); - -/* sink-harness */ - -GST_CHECK_API -void gst_harness_add_sink_harness (GstHarness * h, - GstHarness * sink_harness); - -GST_CHECK_API -void gst_harness_add_sink (GstHarness * h, - const gchar * sink_element_name); - -GST_CHECK_API -void gst_harness_add_sink_parse (GstHarness * h, - const gchar * launchline); - -GST_CHECK_API -GstFlowReturn gst_harness_push_to_sink (GstHarness * h); - -GST_CHECK_API -GstFlowReturn gst_harness_sink_push_many (GstHarness * h, gint pushes); - -/* convenience functions */ - -GST_CHECK_API -GstElement * gst_harness_find_element (GstHarness * h, - const gchar * element_name); - -GST_CHECK_API -void gst_harness_set (GstHarness * h, - const gchar * element_name, - const gchar * first_property_name, ...) G_GNUC_NULL_TERMINATED; - -GST_CHECK_API -void gst_harness_get (GstHarness * h, - const gchar * element_name, - const gchar * first_property_name, ...) G_GNUC_NULL_TERMINATED; - -GST_CHECK_API -void gst_harness_add_probe (GstHarness * h, - const gchar * element_name, - const gchar * pad_name, - GstPadProbeType mask, - GstPadProbeCallback callback, - gpointer user_data, - GDestroyNotify destroy_data); - -/* Stress */ - -GST_CHECK_API -guint gst_harness_stress_thread_stop (GstHarnessThread * t); - -GST_CHECK_API -GstHarnessThread * gst_harness_stress_custom_start (GstHarness * h, - GFunc init, - GFunc callback, - gpointer data, - gulong sleep); - -#define gst_harness_stress_statechange_start(h) \ - gst_harness_stress_statechange_start_full (h, G_USEC_PER_SEC / 100) - -GST_CHECK_API -GstHarnessThread * gst_harness_stress_statechange_start_full (GstHarness * h, - gulong sleep); - -#define gst_harness_stress_push_buffer_start(h, c, s, b) \ - gst_harness_stress_push_buffer_start_full (h, c, s, b, 0) - -GST_CHECK_API -GstHarnessThread * gst_harness_stress_push_buffer_start_full (GstHarness * h, - GstCaps * caps, - const GstSegment * segment, - GstBuffer * buf, - gulong sleep); - -/** - * GstHarnessPrepareBufferFunc: - * @h: a #GstHarness - * @data: user data - * - * Since: 1.6 - */ -typedef GstBuffer * (*GstHarnessPrepareBufferFunc) (GstHarness * h, gpointer data); - -#define gst_harness_stress_push_buffer_with_cb_start(h, c, s, f, d, n) \ - gst_harness_stress_push_buffer_with_cb_start_full (h, c, s, f, d, n, 0) - -GST_CHECK_API -GstHarnessThread * gst_harness_stress_push_buffer_with_cb_start_full (GstHarness * h, - GstCaps * caps, - const GstSegment * segment, - GstHarnessPrepareBufferFunc func, - gpointer data, - GDestroyNotify notify, - gulong sleep); - -#define gst_harness_stress_push_event_start(h, e) \ - gst_harness_stress_push_event_start_full (h, e, 0) - -GST_CHECK_API -GstHarnessThread * gst_harness_stress_push_event_start_full (GstHarness * h, - GstEvent * event, - gulong sleep); - -/** - * GstHarnessPrepareEventFunc: - * @h: a #GstHarness - * @data: user data - * - * Since: 1.8 - */ -typedef GstEvent * (*GstHarnessPrepareEventFunc) (GstHarness * h, gpointer data); - -#define gst_harness_stress_push_event_with_cb_start(h, f, d, n) \ - gst_harness_stress_push_event_with_cb_start_full (h, f, d, n, 0) - -GST_CHECK_API -GstHarnessThread * gst_harness_stress_push_event_with_cb_start_full (GstHarness * h, - GstHarnessPrepareEventFunc func, - gpointer data, - GDestroyNotify notify, - gulong sleep); - -#define gst_harness_stress_send_upstream_event_start(h, e) \ - gst_harness_stress_push_upstream_event_start_full (h, e, 0) - -GST_CHECK_API -GstHarnessThread * gst_harness_stress_push_upstream_event_start_full (GstHarness * h, - GstEvent * event, - gulong sleep); - -#define gst_harness_stress_send_upstream_event_with_cb_start(h, f, d, n) \ - gst_harness_stress_push_upstream_event_with_cb_start_full (h, f, d, n, 0) - -GST_CHECK_API -GstHarnessThread * gst_harness_stress_push_upstream_event_with_cb_start_full (GstHarness * h, - GstHarnessPrepareEventFunc func, - gpointer data, - GDestroyNotify notify, - gulong sleep); - - -#define gst_harness_stress_property_start(h, n, v) \ - gst_harness_stress_property_start_full (h, n, v, G_USEC_PER_SEC / 1000) - -GST_CHECK_API -GstHarnessThread * gst_harness_stress_property_start_full (GstHarness * h, - const gchar * name, - const GValue * value, - gulong sleep); - -#define gst_harness_stress_requestpad_start(h, t, n, c, r) \ - gst_harness_stress_requestpad_start_full (h, t, n, c, r, G_USEC_PER_SEC / 100) - -GST_CHECK_API -GstHarnessThread * gst_harness_stress_requestpad_start_full (GstHarness * h, - GstPadTemplate * templ, - const gchar * name, - GstCaps * caps, - gboolean release, - gulong sleep); - -G_END_DECLS - -#endif /* __GST_HARNESS_H__ */ diff --git a/libs/gst/check/gsttestclock.c b/libs/gst/check/gsttestclock.c deleted file mode 100644 index 83aadc426f..0000000000 --- a/libs/gst/check/gsttestclock.c +++ /dev/null @@ -1,1234 +0,0 @@ -/* GstTestClock - A deterministic clock for GStreamer unit tests - * - * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com> - * Copyright (C) 2012 Sebastian Rasmussen <sebastian.rasmussen@axis.com> - * Copyright (C) 2012 Havard Graff <havard@pexip.com> - * Copyright (C) 2013 Haakon Sporsheim <haakon@pexip.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/** - * SECTION:gsttestclock - * @title: GstTestClock - * @short_description: Controllable, deterministic clock for GStreamer unit tests - * @see_also: #GstSystemClock, #GstClock - * - * GstTestClock is an implementation of #GstClock which has different - * behaviour compared to #GstSystemClock. Time for #GstSystemClock advances - * according to the system time, while time for #GstTestClock changes only - * when gst_test_clock_set_time() or gst_test_clock_advance_time() are - * called. #GstTestClock provides unit tests with the possibility to - * precisely advance the time in a deterministic manner, independent of the - * system time or any other external factors. - * - * ## Advancing the time of a #GstTestClock - * - * |[<!-- language="C" --> - * #include <gst/gst.h> - * #include <gst/check/gsttestclock.h> - * - * GstClock *clock; - * GstTestClock *test_clock; - * - * clock = gst_test_clock_new (); - * test_clock = GST_TEST_CLOCK (clock); - * GST_INFO ("Time: %" GST_TIME_FORMAT, GST_TIME_ARGS (gst_clock_get_time (clock))); - * gst_test_clock_advance_time ( test_clock, 1 * GST_SECOND); - * GST_INFO ("Time: %" GST_TIME_FORMAT, GST_TIME_ARGS (gst_clock_get_time (clock))); - * g_usleep (10 * G_USEC_PER_SEC); - * GST_INFO ("Time: %" GST_TIME_FORMAT, GST_TIME_ARGS (gst_clock_get_time (clock))); - * gst_test_clock_set_time (test_clock, 42 * GST_SECOND); - * GST_INFO ("Time: %" GST_TIME_FORMAT, GST_TIME_ARGS (gst_clock_get_time (clock))); - * ... - * ]| - * - * #GstClock allows for setting up single shot or periodic clock notifications - * as well as waiting for these notifications synchronously (using - * gst_clock_id_wait()) or asynchronously (using gst_clock_id_wait_async() or - * gst_clock_id_wait_async()). This is used by many GStreamer elements, - * among them #GstBaseSrc and #GstBaseSink. - * - * #GstTestClock keeps track of these clock notifications. By calling - * gst_test_clock_wait_for_next_pending_id() or - * gst_test_clock_wait_for_multiple_pending_ids() a unit tests may wait for the - * next one or several clock notifications to be requested. Additionally unit - * tests may release blocked waits in a controlled fashion by calling - * gst_test_clock_process_next_clock_id(). This way a unit test can control the - * inaccuracy (jitter) of clock notifications, since the test can decide to - * release blocked waits when the clock time has advanced exactly to, or past, - * the requested clock notification time. - * - * There are also interfaces for determining if a notification belongs to a - * #GstTestClock or not, as well as getting the number of requested clock - * notifications so far. - * - * N.B.: When a unit test waits for a certain amount of clock notifications to - * be requested in gst_test_clock_wait_for_next_pending_id() or - * gst_test_clock_wait_for_multiple_pending_ids() then these functions may block - * for a long time. If they block forever then the expected clock notifications - * were never requested from #GstTestClock, and so the assumptions in the code - * of the unit test are wrong. The unit test case runner in gstcheck is - * expected to catch these cases either by the default test case timeout or the - * one set for the unit test by calling tcase_set_timeout\(\). - * - * The sample code below assumes that the element under test will delay a - * buffer pushed on the source pad by some latency until it arrives on the sink - * pad. Moreover it is assumed that the element will at some point call - * gst_clock_id_wait() to synchronously wait for a specific time. The first - * buffer sent will arrive exactly on time only delayed by the latency. The - * second buffer will arrive a little late (7ms) due to simulated jitter in the - * clock notification. - * - * ## Demonstration of how to work with clock notifications and #GstTestClock - * - * |[<!-- language="C" --> - * #include <gst/gst.h> - * #include <gst/check/gstcheck.h> - * #include <gst/check/gsttestclock.h> - * - * GstClockTime latency; - * GstElement *element; - * GstPad *srcpad; - * GstClock *clock; - * GstTestClock *test_clock; - * GstBuffer buf; - * GstClockID pending_id; - * GstClockID processed_id; - * - * latency = 42 * GST_MSECOND; - * element = create_element (latency, ...); - * srcpad = get_source_pad (element); - * - * clock = gst_test_clock_new (); - * test_clock = GST_TEST_CLOCK (clock); - * gst_element_set_clock (element, clock); - * - * GST_INFO ("Set time, create and push the first buffer\n"); - * gst_test_clock_set_time (test_clock, 0); - * buf = create_test_buffer (gst_clock_get_time (clock), ...); - * gst_assert_cmpint (gst_pad_push (srcpad, buf), ==, GST_FLOW_OK); - * - * GST_INFO ("Block until element is waiting for a clock notification\n"); - * gst_test_clock_wait_for_next_pending_id (test_clock, &pending_id); - * GST_INFO ("Advance to the requested time of the clock notification\n"); - * gst_test_clock_advance_time (test_clock, latency); - * GST_INFO ("Release the next blocking wait and make sure it is the one from element\n"); - * processed_id = gst_test_clock_process_next_clock_id (test_clock); - * g_assert (processed_id == pending_id); - * g_assert_cmpint (GST_CLOCK_ENTRY_STATUS (processed_id), ==, GST_CLOCK_OK); - * gst_clock_id_unref (pending_id); - * gst_clock_id_unref (processed_id); - * - * GST_INFO ("Validate that element produced an output buffer and check its timestamp\n"); - * g_assert_cmpint (get_number_of_output_buffer (...), ==, 1); - * buf = get_buffer_pushed_by_element (element, ...); - * g_assert_cmpint (GST_BUFFER_TIMESTAMP (buf), ==, latency); - * gst_buffer_unref (buf); - * GST_INFO ("Check that element does not wait for any clock notification\n"); - * g_assert (!gst_test_clock_peek_next_pending_id (test_clock, NULL)); - * - * GST_INFO ("Set time, create and push the second buffer\n"); - * gst_test_clock_advance_time (test_clock, 10 * GST_SECOND); - * buf = create_test_buffer (gst_clock_get_time (clock), ...); - * gst_assert_cmpint (gst_pad_push (srcpad, buf), ==, GST_FLOW_OK); - * - * GST_INFO ("Block until element is waiting for a new clock notification\n"); - * (gst_test_clock_wait_for_next_pending_id (test_clock, &pending_id); - * GST_INFO ("Advance past 7ms beyond the requested time of the clock notification\n"); - * gst_test_clock_advance_time (test_clock, latency + 7 * GST_MSECOND); - * GST_INFO ("Release the next blocking wait and make sure it is the one from element\n"); - * processed_id = gst_test_clock_process_next_clock_id (test_clock); - * g_assert (processed_id == pending_id); - * g_assert_cmpint (GST_CLOCK_ENTRY_STATUS (processed_id), ==, GST_CLOCK_OK); - * gst_clock_id_unref (pending_id); - * gst_clock_id_unref (processed_id); - * - * GST_INFO ("Validate that element produced an output buffer and check its timestamp\n"); - * g_assert_cmpint (get_number_of_output_buffer (...), ==, 1); - * buf = get_buffer_pushed_by_element (element, ...); - * g_assert_cmpint (GST_BUFFER_TIMESTAMP (buf), ==, - * 10 * GST_SECOND + latency + 7 * GST_MSECOND); - * gst_buffer_unref (buf); - * GST_INFO ("Check that element does not wait for any clock notification\n"); - * g_assert (!gst_test_clock_peek_next_pending_id (test_clock, NULL)); - * ... - * ]| - * - * Since #GstTestClock is only supposed to be used in unit tests it calls - * g_assert(), g_assert_cmpint() or g_assert_cmpuint() to validate all function - * arguments. This will highlight any issues with the unit test code itself. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "gsttestclock.h" - -enum -{ - PROP_0, - PROP_START_TIME, - PROP_CLOCK_TYPE -}; - -typedef struct _GstClockEntryContext GstClockEntryContext; - -struct _GstClockEntryContext -{ - GstClockEntry *clock_entry; - GstClockTimeDiff time_diff; -}; - -struct _GstTestClockPrivate -{ - GstClockType clock_type; - GstClockTime start_time; - GstClockTime internal_time; - GList *entry_contexts; - GCond entry_added_cond; - GCond entry_processed_cond; -}; - -#define DEFAULT_CLOCK_TYPE GST_CLOCK_TYPE_MONOTONIC - -#define GST_TEST_CLOCK_GET_PRIVATE(obj) ((GST_TEST_CLOCK_CAST (obj))->priv) - -GST_DEBUG_CATEGORY_STATIC (test_clock_debug); -#define GST_CAT_TEST_CLOCK test_clock_debug - -#define _do_init \ -G_STMT_START { \ - GST_DEBUG_CATEGORY_INIT (test_clock_debug, "GST_TEST_CLOCK", \ - GST_DEBUG_BOLD, "Test clocks for unit tests"); \ -} G_STMT_END - -G_DEFINE_TYPE_WITH_CODE (GstTestClock, gst_test_clock, - GST_TYPE_CLOCK, G_ADD_PRIVATE (GstTestClock) _do_init); - -static GstObjectClass *parent_class = NULL; - -static void gst_test_clock_constructed (GObject * object); -static void gst_test_clock_dispose (GObject * object); -static void gst_test_clock_finalize (GObject * object); -static void gst_test_clock_get_property (GObject * object, guint property_id, - GValue * value, GParamSpec * pspec); -static void gst_test_clock_set_property (GObject * object, guint property_id, - const GValue * value, GParamSpec * pspec); - -static GstClockTime gst_test_clock_get_resolution (GstClock * clock); -static GstClockTime gst_test_clock_get_internal_time (GstClock * clock); -static GstClockReturn gst_test_clock_wait (GstClock * clock, - GstClockEntry * entry, GstClockTimeDiff * jitter); -static GstClockReturn gst_test_clock_wait_async (GstClock * clock, - GstClockEntry * entry); -static void gst_test_clock_unschedule (GstClock * clock, GstClockEntry * entry); - -static gboolean gst_test_clock_peek_next_pending_id_unlocked (GstTestClock * - test_clock, GstClockID * pending_id); -static guint gst_test_clock_peek_id_count_unlocked (GstTestClock * test_clock); - -static void gst_test_clock_add_entry (GstTestClock * test_clock, - GstClockEntry * entry, GstClockTimeDiff * jitter); -static void gst_test_clock_remove_entry (GstTestClock * test_clock, - GstClockEntry * entry); -static GstClockEntryContext *gst_test_clock_lookup_entry_context (GstTestClock * - test_clock, GstClockEntry * clock_entry); - -static gint gst_clock_entry_context_compare_func (gconstpointer a, - gconstpointer b); - -static void -gst_test_clock_class_init (GstTestClockClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GstClockClass *gstclock_class = GST_CLOCK_CLASS (klass); - GParamSpec *pspec; - - parent_class = g_type_class_peek_parent (klass); - - gobject_class->constructed = GST_DEBUG_FUNCPTR (gst_test_clock_constructed); - gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_test_clock_dispose); - gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_test_clock_finalize); - gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_test_clock_get_property); - gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_test_clock_set_property); - - gstclock_class->get_resolution = - GST_DEBUG_FUNCPTR (gst_test_clock_get_resolution); - gstclock_class->get_internal_time = - GST_DEBUG_FUNCPTR (gst_test_clock_get_internal_time); - gstclock_class->wait = GST_DEBUG_FUNCPTR (gst_test_clock_wait); - gstclock_class->wait_async = GST_DEBUG_FUNCPTR (gst_test_clock_wait_async); - gstclock_class->unschedule = GST_DEBUG_FUNCPTR (gst_test_clock_unschedule); - - /** - * GstTestClock:start-time: - * - * When a #GstTestClock is constructed it will have a certain start time set. - * If the clock was created using gst_test_clock_new_with_start_time() then - * this property contains the value of the @start_time argument. If - * gst_test_clock_new() was called the clock started at time zero, and thus - * this property contains the value 0. - */ - pspec = g_param_spec_uint64 ("start-time", "Start Time", - "Start Time of the Clock", 0, G_MAXUINT64, 0, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); - g_object_class_install_property (gobject_class, PROP_START_TIME, pspec); - - g_object_class_install_property (gobject_class, PROP_CLOCK_TYPE, - g_param_spec_enum ("clock-type", "Clock type", - "The kind of clock implementation to be reported by this clock", - GST_TYPE_CLOCK_TYPE, DEFAULT_CLOCK_TYPE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - -} - -static void -gst_test_clock_init (GstTestClock * test_clock) -{ - GstTestClockPrivate *priv; - - test_clock->priv = gst_test_clock_get_instance_private (test_clock); - - priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - - g_cond_init (&priv->entry_added_cond); - g_cond_init (&priv->entry_processed_cond); - priv->clock_type = DEFAULT_CLOCK_TYPE; - - GST_OBJECT_FLAG_SET (test_clock, - GST_CLOCK_FLAG_CAN_DO_SINGLE_SYNC | - GST_CLOCK_FLAG_CAN_DO_SINGLE_ASYNC | - GST_CLOCK_FLAG_CAN_DO_PERIODIC_SYNC | - GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC); -} - -static void -gst_test_clock_constructed (GObject * object) -{ - GstTestClock *test_clock = GST_TEST_CLOCK (object); - GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - - priv->internal_time = priv->start_time; - - G_OBJECT_CLASS (parent_class)->constructed (object); -} - -static void -gst_test_clock_dispose (GObject * object) -{ - GstTestClock *test_clock = GST_TEST_CLOCK (object); - GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - - GST_OBJECT_LOCK (test_clock); - - while (priv->entry_contexts != NULL) { - GstClockEntryContext *ctx = priv->entry_contexts->data; - gst_test_clock_remove_entry (test_clock, ctx->clock_entry); - } - - GST_OBJECT_UNLOCK (test_clock); - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -gst_test_clock_finalize (GObject * object) -{ - GstTestClock *test_clock = GST_TEST_CLOCK (object); - GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - - g_cond_clear (&priv->entry_added_cond); - g_cond_clear (&priv->entry_processed_cond); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_test_clock_get_property (GObject * object, guint property_id, - GValue * value, GParamSpec * pspec) -{ - GstTestClock *test_clock = GST_TEST_CLOCK (object); - GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - - switch (property_id) { - case PROP_START_TIME: - g_value_set_uint64 (value, priv->start_time); - break; - case PROP_CLOCK_TYPE: - g_value_set_enum (value, priv->clock_type); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gst_test_clock_set_property (GObject * object, guint property_id, - const GValue * value, GParamSpec * pspec) -{ - GstTestClock *test_clock = GST_TEST_CLOCK (object); - GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - - switch (property_id) { - case PROP_START_TIME: - priv->start_time = g_value_get_uint64 (value); - GST_CAT_TRACE_OBJECT (GST_CAT_TEST_CLOCK, test_clock, - "test clock start time initialized at %" GST_TIME_FORMAT, - GST_TIME_ARGS (priv->start_time)); - break; - case PROP_CLOCK_TYPE: - priv->clock_type = (GstClockType) g_value_get_enum (value); - GST_CAT_DEBUG (GST_CAT_TEST_CLOCK, "clock-type set to %d", - priv->clock_type); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static GstClockTime -gst_test_clock_get_resolution (GstClock * clock) -{ - (void) clock; - return 1; -} - -static GstClockTime -gst_test_clock_get_internal_time (GstClock * clock) -{ - GstTestClock *test_clock = GST_TEST_CLOCK (clock); - GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - GstClockTime result; - - GST_OBJECT_LOCK (test_clock); - - GST_CAT_TRACE_OBJECT (GST_CAT_TEST_CLOCK, test_clock, - "retrieving test clock time %" GST_TIME_FORMAT, - GST_TIME_ARGS (priv->internal_time)); - result = priv->internal_time; - - GST_OBJECT_UNLOCK (test_clock); - - return result; -} - -static GstClockReturn -gst_test_clock_wait (GstClock * clock, - GstClockEntry * entry, GstClockTimeDiff * jitter) -{ - GstTestClock *test_clock = GST_TEST_CLOCK (clock); - GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - - GST_OBJECT_LOCK (test_clock); - - GST_CAT_DEBUG_OBJECT (GST_CAT_TEST_CLOCK, test_clock, - "requesting synchronous clock notification at %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_CLOCK_ENTRY_TIME (entry))); - - if (GST_CLOCK_ENTRY_STATUS (entry) == GST_CLOCK_UNSCHEDULED) - goto was_unscheduled; - - if (gst_test_clock_lookup_entry_context (test_clock, entry) == NULL) - gst_test_clock_add_entry (test_clock, entry, jitter); - - GST_CLOCK_ENTRY_STATUS (entry) = GST_CLOCK_BUSY; - - while (GST_CLOCK_ENTRY_STATUS (entry) == GST_CLOCK_BUSY) - g_cond_wait (&priv->entry_processed_cond, GST_OBJECT_GET_LOCK (test_clock)); - - GST_OBJECT_UNLOCK (test_clock); - - return GST_CLOCK_ENTRY_STATUS (entry); - - /* ERRORS */ -was_unscheduled: - { - GST_CAT_DEBUG_OBJECT (GST_CAT_TEST_CLOCK, test_clock, - "entry was unscheduled"); - GST_OBJECT_UNLOCK (test_clock); - return GST_CLOCK_UNSCHEDULED; - } -} - -static GstClockReturn -gst_test_clock_wait_async (GstClock * clock, GstClockEntry * entry) -{ - GstTestClock *test_clock = GST_TEST_CLOCK (clock); - - GST_OBJECT_LOCK (test_clock); - - if (GST_CLOCK_ENTRY_STATUS (entry) == GST_CLOCK_UNSCHEDULED) - goto was_unscheduled; - - GST_CAT_DEBUG_OBJECT (GST_CAT_TEST_CLOCK, test_clock, - "requesting asynchronous clock notification at %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_CLOCK_ENTRY_TIME (entry))); - - gst_test_clock_add_entry (test_clock, entry, NULL); - - GST_OBJECT_UNLOCK (test_clock); - - return GST_CLOCK_OK; - - /* ERRORS */ -was_unscheduled: - { - GST_CAT_DEBUG_OBJECT (GST_CAT_TEST_CLOCK, test_clock, - "entry was unscheduled"); - GST_OBJECT_UNLOCK (test_clock); - return GST_CLOCK_UNSCHEDULED; - } -} - -static void -gst_test_clock_unschedule (GstClock * clock, GstClockEntry * entry) -{ - GstTestClock *test_clock = GST_TEST_CLOCK (clock); - - GST_OBJECT_LOCK (test_clock); - - GST_CAT_DEBUG_OBJECT (GST_CAT_TEST_CLOCK, test_clock, - "unscheduling requested clock notification at %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_CLOCK_ENTRY_TIME (entry))); - - GST_CLOCK_ENTRY_STATUS (entry) = GST_CLOCK_UNSCHEDULED; - gst_test_clock_remove_entry (test_clock, entry); - - GST_OBJECT_UNLOCK (test_clock); -} - -static gboolean -gst_test_clock_peek_next_pending_id_unlocked (GstTestClock * test_clock, - GstClockID * pending_id) -{ - GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - GList *imminent_clock_id = g_list_first (priv->entry_contexts); - gboolean result = FALSE; - - if (imminent_clock_id != NULL) { - GstClockEntryContext *ctx = imminent_clock_id->data; - - if (pending_id != NULL) { - *pending_id = gst_clock_id_ref (ctx->clock_entry); - } - - result = TRUE; - } - - return result; -} - -static guint -gst_test_clock_peek_id_count_unlocked (GstTestClock * test_clock) -{ - GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - - return g_list_length (priv->entry_contexts); -} - -static void -gst_test_clock_add_entry (GstTestClock * test_clock, - GstClockEntry * entry, GstClockTimeDiff * jitter) -{ - GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - GstClockTime now; - GstClockEntryContext *ctx; - - now = gst_clock_adjust_unlocked (GST_CLOCK (test_clock), priv->internal_time); - - if (jitter != NULL) - *jitter = GST_CLOCK_DIFF (GST_CLOCK_ENTRY_TIME (entry), now); - - ctx = g_slice_new (GstClockEntryContext); - ctx->clock_entry = GST_CLOCK_ENTRY (gst_clock_id_ref (entry)); - ctx->time_diff = GST_CLOCK_DIFF (now, GST_CLOCK_ENTRY_TIME (entry)); - - priv->entry_contexts = g_list_insert_sorted (priv->entry_contexts, ctx, - gst_clock_entry_context_compare_func); - - g_cond_broadcast (&priv->entry_added_cond); -} - -static void -gst_test_clock_remove_entry (GstTestClock * test_clock, GstClockEntry * entry) -{ - GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - GstClockEntryContext *ctx; - - ctx = gst_test_clock_lookup_entry_context (test_clock, entry); - if (ctx != NULL) { - gst_clock_id_unref (ctx->clock_entry); - priv->entry_contexts = g_list_remove (priv->entry_contexts, ctx); - g_slice_free (GstClockEntryContext, ctx); - - g_cond_broadcast (&priv->entry_processed_cond); - } -} - -static GstClockEntryContext * -gst_test_clock_lookup_entry_context (GstTestClock * test_clock, - GstClockEntry * clock_entry) -{ - GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - GstClockEntryContext *result = NULL; - GList *cur; - - for (cur = priv->entry_contexts; cur != NULL; cur = cur->next) { - GstClockEntryContext *ctx = cur->data; - - if (ctx->clock_entry == clock_entry) { - result = ctx; - break; - } - } - - return result; -} - -static gint -gst_clock_entry_context_compare_func (gconstpointer a, gconstpointer b) -{ - const GstClockEntryContext *ctx_a = a; - const GstClockEntryContext *ctx_b = b; - - return gst_clock_id_compare_func (ctx_a->clock_entry, ctx_b->clock_entry); -} - -static void -process_entry_context_unlocked (GstTestClock * test_clock, - GstClockEntryContext * ctx) -{ - GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - GstClockEntry *entry = ctx->clock_entry; - - if (ctx->time_diff >= 0) - GST_CLOCK_ENTRY_STATUS (entry) = GST_CLOCK_OK; - else - GST_CLOCK_ENTRY_STATUS (entry) = GST_CLOCK_EARLY; - - if (entry->func != NULL) { - GST_OBJECT_UNLOCK (test_clock); - entry->func (GST_CLOCK (test_clock), priv->internal_time, entry, - entry->user_data); - GST_OBJECT_LOCK (test_clock); - } - - gst_test_clock_remove_entry (test_clock, entry); - - if (GST_CLOCK_ENTRY_TYPE (entry) == GST_CLOCK_ENTRY_PERIODIC) { - GST_CLOCK_ENTRY_TIME (entry) += GST_CLOCK_ENTRY_INTERVAL (entry); - - if (entry->func != NULL) - gst_test_clock_add_entry (test_clock, entry, NULL); - } -} - -static GList * -gst_test_clock_get_pending_id_list_unlocked (GstTestClock * test_clock) -{ - GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - GQueue queue = G_QUEUE_INIT; - GList *cur; - - for (cur = priv->entry_contexts; cur != NULL; cur = cur->next) { - GstClockEntryContext *ctx = cur->data; - - g_queue_push_tail (&queue, gst_clock_id_ref (ctx->clock_entry)); - } - - return queue.head; -} - -/** - * gst_test_clock_new: - * - * Creates a new test clock with its time set to zero. - * - * MT safe. - * - * Returns: (transfer full): a #GstTestClock cast to #GstClock. - * - * Since: 1.2 - */ -GstClock * -gst_test_clock_new (void) -{ - return gst_test_clock_new_with_start_time (0); -} - -/** - * gst_test_clock_new_with_start_time: - * @start_time: a #GstClockTime set to the desired start time of the clock. - * - * Creates a new test clock with its time set to the specified time. - * - * MT safe. - * - * Returns: (transfer full): a #GstTestClock cast to #GstClock. - * - * Since: 1.2 - */ -GstClock * -gst_test_clock_new_with_start_time (GstClockTime start_time) -{ - GstClock *clock; - - g_assert_cmpuint (start_time, !=, GST_CLOCK_TIME_NONE); - clock = g_object_new (GST_TYPE_TEST_CLOCK, "start-time", start_time, NULL); - - /* Clear floating flag */ - gst_object_ref_sink (clock); - - return clock; -} - -/** - * gst_test_clock_set_time: - * @test_clock: a #GstTestClock of which to set the time - * @new_time: a #GstClockTime later than that returned by gst_clock_get_time() - * - * Sets the time of @test_clock to the time given by @new_time. The time of - * @test_clock is monotonically increasing, therefore providing a @new_time - * which is earlier or equal to the time of the clock as given by - * gst_clock_get_time() is a programming error. - * - * MT safe. - * - * Since: 1.2 - */ -void -gst_test_clock_set_time (GstTestClock * test_clock, GstClockTime new_time) -{ - GstTestClockPrivate *priv; - - g_return_if_fail (GST_IS_TEST_CLOCK (test_clock)); - - priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - - g_assert_cmpuint (new_time, !=, GST_CLOCK_TIME_NONE); - - GST_OBJECT_LOCK (test_clock); - - g_assert_cmpuint (new_time, >=, priv->internal_time); - - priv->internal_time = new_time; - GST_CAT_DEBUG_OBJECT (GST_CAT_TEST_CLOCK, test_clock, - "clock set to %" GST_TIME_FORMAT, GST_TIME_ARGS (new_time)); - - GST_OBJECT_UNLOCK (test_clock); -} - -/** - * gst_test_clock_advance_time: - * @test_clock: a #GstTestClock for which to increase the time - * @delta: a positive #GstClockTimeDiff to be added to the time of the clock - * - * Advances the time of the @test_clock by the amount given by @delta. The - * time of @test_clock is monotonically increasing, therefore providing a - * @delta which is negative or zero is a programming error. - * - * MT safe. - * - * Since: 1.2 - */ -void -gst_test_clock_advance_time (GstTestClock * test_clock, GstClockTimeDiff delta) -{ - GstTestClockPrivate *priv; - - g_return_if_fail (GST_IS_TEST_CLOCK (test_clock)); - - priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - - g_assert_cmpint (delta, >=, 0); - g_assert_cmpuint (delta, <, G_MAXUINT64 - delta); - - GST_OBJECT_LOCK (test_clock); - - GST_CAT_DEBUG_OBJECT (GST_CAT_TEST_CLOCK, test_clock, - "advancing clock by %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT, - GST_TIME_ARGS (delta), GST_TIME_ARGS (priv->internal_time + delta)); - priv->internal_time += delta; - - GST_OBJECT_UNLOCK (test_clock); -} - -/** - * gst_test_clock_peek_id_count: - * @test_clock: a #GstTestClock for which to count notifications - * - * Determine the number of pending clock notifications that have been - * requested from the @test_clock. - * - * MT safe. - * - * Returns: the number of pending clock notifications. - * - * Since: 1.2 - */ -guint -gst_test_clock_peek_id_count (GstTestClock * test_clock) -{ - guint result; - - g_return_val_if_fail (GST_IS_TEST_CLOCK (test_clock), 0); - - GST_OBJECT_LOCK (test_clock); - result = gst_test_clock_peek_id_count_unlocked (test_clock); - GST_OBJECT_UNLOCK (test_clock); - - return result; -} - -/** - * gst_test_clock_has_id: - * @test_clock: a #GstTestClock to ask if it provided the notification - * @id: (transfer none): a #GstClockID clock notification - * - * Checks whether @test_clock was requested to provide the clock notification - * given by @id. - * - * MT safe. - * - * Returns: %TRUE if the clock has been asked to provide the given clock - * notification, %FALSE otherwise. - * - * Since: 1.2 - */ -gboolean -gst_test_clock_has_id (GstTestClock * test_clock, GstClockID id) -{ - gboolean result; - - g_return_val_if_fail (GST_IS_TEST_CLOCK (test_clock), FALSE); - g_assert (id != NULL); - - GST_OBJECT_LOCK (test_clock); - result = gst_test_clock_lookup_entry_context (test_clock, id) != NULL; - GST_OBJECT_UNLOCK (test_clock); - - return result; -} - -/** - * gst_test_clock_peek_next_pending_id: - * @test_clock: a #GstTestClock to check the clock notifications for - * @pending_id: (allow-none) (out) (transfer full): a #GstClockID clock - * notification to look for - * - * Determines if the @pending_id is the next clock notification scheduled to - * be triggered given the current time of the @test_clock. - * - * MT safe. - * - * Return: %TRUE if @pending_id is the next clock notification to be - * triggered, %FALSE otherwise. - * - * Since: 1.2 - */ -gboolean -gst_test_clock_peek_next_pending_id (GstTestClock * test_clock, - GstClockID * pending_id) -{ - gboolean result; - - g_return_val_if_fail (GST_IS_TEST_CLOCK (test_clock), FALSE); - - GST_OBJECT_LOCK (test_clock); - result = gst_test_clock_peek_next_pending_id_unlocked (test_clock, - pending_id); - GST_OBJECT_UNLOCK (test_clock); - - return result; -} - -/** - * gst_test_clock_wait_for_next_pending_id: - * @test_clock: #GstTestClock for which to get the pending clock notification - * @pending_id: (allow-none) (out) (transfer full): #GstClockID - * with information about the pending clock notification - * - * Waits until a clock notification is requested from @test_clock. There is no - * timeout for this wait, see the main description of #GstTestClock. A reference - * to the pending clock notification is stored in @pending_id. - * - * MT safe. - * - * Since: 1.2 - */ -void -gst_test_clock_wait_for_next_pending_id (GstTestClock * test_clock, - GstClockID * pending_id) -{ - GstTestClockPrivate *priv; - - g_return_if_fail (GST_IS_TEST_CLOCK (test_clock)); - - priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - - GST_OBJECT_LOCK (test_clock); - - while (priv->entry_contexts == NULL) - g_cond_wait (&priv->entry_added_cond, GST_OBJECT_GET_LOCK (test_clock)); - - if (!gst_test_clock_peek_next_pending_id_unlocked (test_clock, pending_id)) - g_assert_not_reached (); - - GST_OBJECT_UNLOCK (test_clock); -} - -/** - * gst_test_clock_wait_for_pending_id_count: - * @test_clock: #GstTestClock for which to await having enough pending clock - * @count: the number of pending clock notifications to wait for - * - * Blocks until at least @count clock notifications have been requested from - * @test_clock. There is no timeout for this wait, see the main description of - * #GstTestClock. - * - * Since: 1.2 - * - * Deprecated: use gst_test_clock_wait_for_multiple_pending_ids() instead. - */ -#ifndef GST_REMOVE_DEPRECATED -void -gst_test_clock_wait_for_pending_id_count (GstTestClock * test_clock, - guint count) -{ - gst_test_clock_wait_for_multiple_pending_ids (test_clock, count, NULL); -} -#endif - -/** - * gst_test_clock_process_next_clock_id: - * @test_clock: a #GstTestClock for which to retrieve the next pending clock - * notification - * - * MT safe. - * - * Returns: (transfer full) (nullable): a #GstClockID containing the next pending clock - * notification. - * - * Since: 1.2 - */ -GstClockID -gst_test_clock_process_next_clock_id (GstTestClock * test_clock) -{ - GstTestClockPrivate *priv; - GstClockID result = NULL; - GstClockEntryContext *ctx = NULL; - GList *cur; - - g_return_val_if_fail (GST_IS_TEST_CLOCK (test_clock), NULL); - - priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - - GST_OBJECT_LOCK (test_clock); - - for (cur = priv->entry_contexts; cur != NULL && result == NULL; - cur = cur->next) { - ctx = cur->data; - - if (priv->internal_time >= GST_CLOCK_ENTRY_TIME (ctx->clock_entry)) - result = gst_clock_id_ref (ctx->clock_entry); - } - - if (result != NULL) - process_entry_context_unlocked (test_clock, ctx); - - GST_OBJECT_UNLOCK (test_clock); - - return result; -} - -/** - * gst_test_clock_get_next_entry_time: - * @test_clock: a #GstTestClock to fetch the next clock notification time for - * - * Retrieve the requested time for the next pending clock notification. - * - * MT safe. - * - * Returns: a #GstClockTime set to the time of the next pending clock - * notification. If no clock notifications have been requested - * %GST_CLOCK_TIME_NONE will be returned. - * - * Since: 1.2 - */ -GstClockTime -gst_test_clock_get_next_entry_time (GstTestClock * test_clock) -{ - GstTestClockPrivate *priv; - GstClockTime result = GST_CLOCK_TIME_NONE; - GList *imminent_clock_id; - - g_return_val_if_fail (GST_IS_TEST_CLOCK (test_clock), GST_CLOCK_TIME_NONE); - - priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - - GST_OBJECT_LOCK (test_clock); - - /* The list of pending clock notifications is sorted by time, - so the most imminent one is the first one in the list. */ - imminent_clock_id = g_list_first (priv->entry_contexts); - if (imminent_clock_id != NULL) { - GstClockEntryContext *ctx = imminent_clock_id->data; - result = GST_CLOCK_ENTRY_TIME (ctx->clock_entry); - } - - GST_OBJECT_UNLOCK (test_clock); - - return result; -} - -/** - * gst_test_clock_wait_for_multiple_pending_ids: - * @test_clock: #GstTestClock for which to await having enough pending clock - * @count: the number of pending clock notifications to wait for - * @pending_list: (out) (element-type Gst.ClockID) (transfer full) (allow-none): Address - * of a #GList pointer variable to store the list of pending #GstClockIDs - * that expired, or %NULL - * - * Blocks until at least @count clock notifications have been requested from - * @test_clock. There is no timeout for this wait, see the main description of - * #GstTestClock. - * - * MT safe. - * - * Since: 1.4 - */ -void -gst_test_clock_wait_for_multiple_pending_ids (GstTestClock * test_clock, - guint count, GList ** pending_list) -{ - GstTestClockPrivate *priv; - - g_return_if_fail (GST_IS_TEST_CLOCK (test_clock)); - priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - - GST_OBJECT_LOCK (test_clock); - - while (g_list_length (priv->entry_contexts) < count) - g_cond_wait (&priv->entry_added_cond, GST_OBJECT_GET_LOCK (test_clock)); - - if (pending_list) - *pending_list = gst_test_clock_get_pending_id_list_unlocked (test_clock); - - GST_OBJECT_UNLOCK (test_clock); -} - -/** - * gst_test_clock_timed_wait_for_multiple_pending_ids: - * @test_clock: #GstTestClock for which to await having enough pending clock - * @count: the number of pending clock notifications to wait for - * @timeout_ms: the timeout in milliseconds - * @pending_list: (out) (element-type Gst.ClockID) (transfer full) (allow-none): Address - * of a #GList pointer variable to store the list of pending #GstClockIDs - * that expired, or %NULL - * - * Blocks until at least @count clock notifications have been requested from - * @test_clock, or the timeout expires. - * - * MT safe. - * - * Returns: a @gboolean %TRUE if the waits have been registered, %FALSE if not. - * (Could be that it timed out waiting or that more waits than waits was found) - * - * Since: 1.16 - */ -gboolean -gst_test_clock_timed_wait_for_multiple_pending_ids (GstTestClock * test_clock, - guint count, guint timeout_ms, GList ** pending_list) -{ - GstTestClockPrivate *priv; - gint64 timeout = g_get_monotonic_time () + - timeout_ms * (G_USEC_PER_SEC / 1000); - gboolean ret; - - g_return_val_if_fail (GST_IS_TEST_CLOCK (test_clock), FALSE); - priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock); - - GST_OBJECT_LOCK (test_clock); - - while (g_list_length (priv->entry_contexts) < count && - g_get_monotonic_time () < timeout) { - g_cond_wait_until (&priv->entry_added_cond, - GST_OBJECT_GET_LOCK (test_clock), timeout); - } - - if (pending_list) - *pending_list = gst_test_clock_get_pending_id_list_unlocked (test_clock); - - ret = (g_list_length (priv->entry_contexts) == count); - - GST_OBJECT_UNLOCK (test_clock); - - return ret; -} - - -/** - * gst_test_clock_process_id: - * @test_clock: #GstTestClock for which to process the pending IDs - * @pending_id: (transfer full): #GstClockID - * - * Processes and releases the pending ID. - * - * MT safe. - * - * Since: 1.18 - */ -gboolean -gst_test_clock_process_id (GstTestClock * test_clock, GstClockID pending_id) -{ - GstClockEntryContext *ctx; - - gboolean result = FALSE; - - g_return_val_if_fail (GST_IS_TEST_CLOCK (test_clock), FALSE); - - GST_OBJECT_LOCK (test_clock); - - ctx = gst_test_clock_lookup_entry_context (test_clock, pending_id); - g_assert (ctx); - - if (ctx) { - process_entry_context_unlocked (test_clock, ctx); - result = TRUE; - gst_clock_id_unref (pending_id); - } - - GST_OBJECT_UNLOCK (test_clock); - - return result; -} - -/** - * gst_test_clock_process_id_list: - * @test_clock: #GstTestClock for which to process the pending IDs - * @pending_list: (element-type Gst.ClockID) (transfer none) (allow-none): List - * of pending #GstClockIDs - * - * Processes and releases the pending IDs in the list. - * - * MT safe. - * - * Since: 1.4 - */ -guint -gst_test_clock_process_id_list (GstTestClock * test_clock, - const GList * pending_list) -{ - const GList *cur; - guint result = 0; - - g_return_val_if_fail (GST_IS_TEST_CLOCK (test_clock), 0); - - GST_OBJECT_LOCK (test_clock); - - for (cur = pending_list; cur != NULL; cur = cur->next) { - GstClockID pending_id = cur->data; - GstClockEntryContext *ctx = - gst_test_clock_lookup_entry_context (test_clock, pending_id); - if (ctx) { - process_entry_context_unlocked (test_clock, ctx); - result++; - } - } - GST_OBJECT_UNLOCK (test_clock); - - return result; -} - -/** - * gst_test_clock_id_list_get_latest_time: - * @pending_list: (element-type Gst.ClockID) (transfer none) (allow-none): List - * of of pending #GstClockIDs - * - * Finds the latest time inside the list. - * - * MT safe. - * - * Since: 1.4 - */ -GstClockTime -gst_test_clock_id_list_get_latest_time (const GList * pending_list) -{ - const GList *cur; - GstClockTime result = 0; - - for (cur = pending_list; cur != NULL; cur = cur->next) { - GstClockID *pending_id = cur->data; - GstClockTime time = gst_clock_id_get_time (pending_id); - if (time > result) - result = time; - } - - return result; -} - -/** - * gst_test_clock_crank: - * @test_clock: #GstTestClock to crank - * - * A "crank" consists of three steps: - * 1: Wait for a #GstClockID to be registered with the #GstTestClock. - * 2: Advance the #GstTestClock to the time the #GstClockID is waiting, unless - * the clock time is already passed the clock id (Since: 1.18). - * 3: Release the #GstClockID wait. - * A "crank" can be though of as the notion of - * manually driving the clock forward to its next logical step. - * - * Return: %TRUE if the crank was successful, %FALSE otherwise. - * - * MT safe. - * - * Since: 1.8 - */ -gboolean -gst_test_clock_crank (GstTestClock * test_clock) -{ - GstClockID res, pending; - GstClockTime now; - gboolean result; - - gst_test_clock_wait_for_next_pending_id (test_clock, &pending); - now = gst_clock_get_time (GST_CLOCK (test_clock)); - if (gst_clock_id_get_time (pending) > now) - gst_test_clock_set_time (test_clock, gst_clock_id_get_time (pending)); - res = gst_test_clock_process_next_clock_id (test_clock); - if (G_LIKELY (res == pending)) { - GST_CAT_DEBUG_OBJECT (GST_CAT_TEST_CLOCK, test_clock, - "cranked to time %" GST_TIME_FORMAT, - GST_TIME_ARGS (gst_clock_get_time (GST_CLOCK (test_clock)))); - result = TRUE; - } else { - GST_CAT_WARNING_OBJECT (GST_CAT_TEST_CLOCK, test_clock, - "testclock next id != pending (%p != %p)", res, pending); - result = FALSE; - } - - if (G_LIKELY (res != NULL)) - gst_clock_id_unref (res); - - gst_clock_id_unref (pending); - - return result; -} diff --git a/libs/gst/check/gsttestclock.h b/libs/gst/check/gsttestclock.h deleted file mode 100644 index a1c1f5a88b..0000000000 --- a/libs/gst/check/gsttestclock.h +++ /dev/null @@ -1,148 +0,0 @@ -/* GstTestClock - A deterministic clock for GStreamer unit tests - * - * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com> - * Copyright (C) 2012 Sebastian Rasmussen <sebastian.rasmussen@axis.com> - * Copyright (C) 2012 Havard Graff <havard@pexip.com> - * Copyright (C) 2013 Haakon Sporsheim <haakon@pexip.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __GST_TEST_CLOCK_H__ -#define __GST_TEST_CLOCK_H__ - -#include <gst/gst.h> -#include <gst/check/check-prelude.h> - -G_BEGIN_DECLS - -#define GST_TYPE_TEST_CLOCK (gst_test_clock_get_type ()) -#define GST_TEST_CLOCK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ - GST_TYPE_TEST_CLOCK, GstTestClock)) -#define GST_IS_TEST_CLOCK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ - GST_TYPE_TEST_CLOCK)) -#define GST_TEST_CLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ - GST_TYPE_TEST_CLOCK, GstTestClockClass)) -#define GST_IS_TEST_CLOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE (\ - (klass), GST_TYPE_TEST_CLOCK)) -#define GST_TEST_CLOCK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS (\ - (obj), GST_TYPE_TEST_CLOCK, GstTestClockClass)) -#define GST_TEST_CLOCK_CAST(obj) ((GstTestClock*)(obj)) - -typedef struct _GstTestClock GstTestClock; -typedef struct _GstTestClockClass GstTestClockClass; -typedef struct _GstTestClockPrivate GstTestClockPrivate; - -/** - * GstTestClock: - * - * A #GstTestClock structure which is based on a #GstClock along with some - * private data. - * - * Since: 1.2 - */ -struct _GstTestClock -{ - GstClock parent; - - /*< private >*/ - GstTestClockPrivate *priv; -}; - -/** - * GstTestClockClass: - * @parent_class: the parent class structure - * - * The class of a #GstTestClock, which has no virtual methods to override. - * - * Since: 1.2 - */ -struct _GstTestClockClass -{ - GstClockClass parent_class; -}; - -GST_CHECK_API -GType gst_test_clock_get_type (void); - -GST_CHECK_API -GstClock * gst_test_clock_new (void); - -GST_CHECK_API -GstClock * gst_test_clock_new_with_start_time (GstClockTime start_time); - -GST_CHECK_API -void gst_test_clock_set_time (GstTestClock * test_clock, - GstClockTime new_time); - -GST_CHECK_API -void gst_test_clock_advance_time (GstTestClock * test_clock, - GstClockTimeDiff delta); - -GST_CHECK_API -guint gst_test_clock_peek_id_count (GstTestClock * test_clock); - -GST_CHECK_API -gboolean gst_test_clock_has_id (GstTestClock * test_clock, GstClockID id); - -GST_CHECK_API -gboolean gst_test_clock_peek_next_pending_id (GstTestClock * test_clock, - GstClockID * pending_id); - -GST_CHECK_API -void gst_test_clock_wait_for_next_pending_id (GstTestClock * test_clock, - GstClockID * pending_id); - -GST_CHECK_DEPRECATED_FOR(gst_test_clock_wait_for_multiple_pending_ids) -void gst_test_clock_wait_for_pending_id_count (GstTestClock * test_clock, - guint count); - -GST_CHECK_API -GstClockID gst_test_clock_process_next_clock_id (GstTestClock * test_clock); - -GST_CHECK_API -GstClockTime gst_test_clock_get_next_entry_time (GstTestClock * test_clock); - -GST_CHECK_API -void gst_test_clock_wait_for_multiple_pending_ids (GstTestClock * test_clock, - guint count, - GList ** pending_list); - -GST_CHECK_API -gboolean gst_test_clock_timed_wait_for_multiple_pending_ids (GstTestClock * test_clock, - guint count, - guint timeout_ms, - GList ** pending_list); - -GST_CHECK_API -gboolean gst_test_clock_process_id (GstTestClock * test_clock, - GstClockID pending_id); - -GST_CHECK_API -guint gst_test_clock_process_id_list (GstTestClock * test_clock, - const GList * pending_list); - -GST_CHECK_API -GstClockTime gst_test_clock_id_list_get_latest_time (const GList * pending_list); - -GST_CHECK_API -gboolean gst_test_clock_crank (GstTestClock * test_clock); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstTestClock, gst_object_unref) - -G_END_DECLS - -#endif /* __GST_TEST_CLOCK_H__ */ diff --git a/libs/gst/check/libcheck/README.txt b/libs/gst/check/libcheck/README.txt deleted file mode 100644 index cc0f2ed27c..0000000000 --- a/libs/gst/check/libcheck/README.txt +++ /dev/null @@ -1,34 +0,0 @@ -This is a copy of libcheck, a unit testing framework for C: - -https://github.com/libcheck/check/ - -The last update was on 9th December, with the following commit: ba42e7de3d62ea9d3699bf0709554b3e47a8f09e - -The check*.c files in this directory are the same as those in the src/ -directory in upstream. The files in the libcompat/ directory are the same as -those in the lib/ directory upstream. - -lib/snprintf.c was omitted since we don't run on any platforms that don't -provide snprintf and the upstream implementation is ~2000 lines. - -lib/malloc.c and lib/realloc.c were omitted since we were doing fine without -them and it does a #define malloc rpl_malloc on Android because the malloc -shipped with Bionic is not GNU-compliant. rpl_malloc is provided by libcheck, -but not everything in gstreamer links against libcheck. We also don't care -about this. - -Steps to sync with upstream: - -1. Clone libcheck from the above git repository -2. Copy files into this directory -3. Run GNU indent on all the code -4. Fix internal #includes -5. Manually inspect the diff -6. Update configure.ac, m4/check-checks.m4, meson.build files, etc -6. Run make check, then commit and push - -Any changes made to files in this directory must be submitted upstream via -a pull request: https://github.com/libcheck/check/compare - -This involves creating an account on GitHub, forking libcheck/check there, -pushing the changes into a branch, and then submitting it as a pull request. diff --git a/libs/gst/check/libcheck/check.c b/libs/gst/check/libcheck/check.c deleted file mode 100644 index 8ef3b62fc3..0000000000 --- a/libs/gst/check/libcheck/check.c +++ /dev/null @@ -1,642 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat/libcompat.h" - -#include <string.h> -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> -#include <math.h> - -#include <glib.h> - -#include "internal-check.h" -#include "check_error.h" -#include "check_list.h" -#include "check_impl.h" -#include "check_msg.h" - -#ifdef HAVE_UNISTD_H -#include <unistd.h> /* for _POSIX_VERSION */ -#endif - -#ifndef DEFAULT_TIMEOUT -#define DEFAULT_TIMEOUT 4 -#endif - -/* - * When a process exits either normally, with exit(), or - * by an uncaught signal, The lower 0x377 bits are passed - * to the parent. Of those, only the lower 8 bits are - * returned by the WEXITSTATUS() macro. - */ -#define WEXITSTATUS_MASK 0xFF - -int check_major_version = CHECK_MAJOR_VERSION; -int check_minor_version = CHECK_MINOR_VERSION; -int check_micro_version = CHECK_MICRO_VERSION; - -const char *current_test_name = NULL; - -static int non_pass (int val); -static Fixture *fixture_create (SFun fun, int ischecked); -static void tcase_add_fixture (TCase * tc, SFun setup, SFun teardown, - int ischecked); -static void tr_init (TestResult * tr); -static void suite_free (Suite * s); -static void tcase_free (TCase * tc); - -Suite * -suite_create (const char *name) -{ - Suite *s; - - s = (Suite *) emalloc (sizeof (Suite)); /* freed in suite_free */ - if (name == NULL) - s->name = ""; - else - s->name = name; - s->tclst = check_list_create (); - return s; -} - -int -suite_tcase (Suite * s, const char *tcname) -{ - List *l; - TCase *tc; - - if (s == NULL) - return 0; - - l = s->tclst; - for (check_list_front (l); !check_list_at_end (l); check_list_advance (l)) { - tc = (TCase *) check_list_val (l); - if (strcmp (tcname, tc->name) == 0) - return 1; - } - - return 0; -} - -static void -suite_free (Suite * s) -{ - List *l; - - if (s == NULL) - return; - l = s->tclst; - for (check_list_front (l); !check_list_at_end (l); check_list_advance (l)) { - tcase_free ((TCase *) check_list_val (l)); - } - check_list_free (s->tclst); - free (s); -} - - -TCase * -tcase_create (const char *name) -{ - char *env; - double timeout_sec = DEFAULT_TIMEOUT; - - TCase *tc = (TCase *) emalloc (sizeof (TCase)); /*freed in tcase_free */ - - if (name == NULL) - tc->name = ""; - else - tc->name = name; - - env = getenv ("CK_DEFAULT_TIMEOUT"); - if (env != NULL) { - char *endptr = NULL; - double tmp = strtod (env, &endptr); - - if (tmp >= 0 && endptr != env && (*endptr) == '\0') { - timeout_sec = tmp; - } - } - - env = getenv ("CK_TIMEOUT_MULTIPLIER"); - if (env != NULL) { - char *endptr = NULL; - double tmp = strtod (env, &endptr); - - if (tmp >= 0 && endptr != env && (*endptr) == '\0') { - timeout_sec = timeout_sec * tmp; - } - } - - tc->timeout.tv_sec = (time_t) floor (timeout_sec); - tc->timeout.tv_nsec = - (long) ((timeout_sec - floor (timeout_sec)) * (double) NANOS_PER_SECONDS); - - tc->tflst = check_list_create (); - tc->unch_sflst = check_list_create (); - tc->ch_sflst = check_list_create (); - tc->unch_tflst = check_list_create (); - tc->ch_tflst = check_list_create (); - tc->tags = check_list_create (); - - return tc; -} - -/* - * Helper function to create a list of tags from - * a space separated string. - */ -List * -tag_string_to_list (const char *tags_string) -{ - List *list; - char *tags; - char *tag; - - list = check_list_create (); - - if (NULL == tags_string) { - return list; - } - - tags = strdup (tags_string); - tag = strtok (tags, " "); - while (tag) { - check_list_add_end (list, strdup (tag)); - tag = strtok (NULL, " "); - } - free (tags); - return list; -} - -void -tcase_set_tags (TCase * tc, const char *tags_orig) -{ - /* replace any pre-existing list */ - if (tc->tags) { - check_list_apply (tc->tags, free); - check_list_free (tc->tags); - } - tc->tags = tag_string_to_list (tags_orig); -} - -static void -tcase_free (TCase * tc) -{ - check_list_apply (tc->tflst, free); - check_list_apply (tc->unch_sflst, free); - check_list_apply (tc->ch_sflst, free); - check_list_apply (tc->unch_tflst, free); - check_list_apply (tc->ch_tflst, free); - check_list_apply (tc->tags, free); - check_list_free (tc->tflst); - check_list_free (tc->unch_sflst); - check_list_free (tc->ch_sflst); - check_list_free (tc->unch_tflst); - check_list_free (tc->ch_tflst); - check_list_free (tc->tags); - free (tc); -} - -unsigned int -tcase_matching_tag (TCase * tc, List * check_for) -{ - - if (NULL == check_for) { - return 0; - } - - for (check_list_front (check_for); !check_list_at_end (check_for); - check_list_advance (check_for)) { - for (check_list_front (tc->tags); !check_list_at_end (tc->tags); - check_list_advance (tc->tags)) { - if (0 == strcmp ((const char *) check_list_val (tc->tags), - (const char *) check_list_val (check_for))) { - return 1; - } - } - } - return 0; -} - -void -suite_add_tcase (Suite * s, TCase * tc) -{ - if (s == NULL || tc == NULL || check_list_contains (s->tclst, tc)) { - return; - } - - check_list_add_end (s->tclst, tc); -} - -void -_tcase_add_test (TCase * tc, TFun fn, const char *name, int _signal, - int allowed_exit_value, int start, int end) -{ - TF *tf; - - if (tc == NULL || fn == NULL || name == NULL) - return; - tf = (TF *) emalloc (sizeof (TF)); /* freed in tcase_free */ - tf->fn = fn; - tf->loop_start = start; - tf->loop_end = end; - tf->signal = _signal; /* 0 means no signal expected */ - tf->allowed_exit_value = (WEXITSTATUS_MASK & allowed_exit_value); /* 0 is default successful exit */ - tf->name = name; - check_list_add_end (tc->tflst, tf); -} - -static Fixture * -fixture_create (SFun fun, int ischecked) -{ - Fixture *f; - - f = (Fixture *) emalloc (sizeof (Fixture)); - f->fun = fun; - f->ischecked = ischecked; - - return f; -} - -void -tcase_add_unchecked_fixture (TCase * tc, SFun setup, SFun teardown) -{ - tcase_add_fixture (tc, setup, teardown, 0); -} - -void -tcase_add_checked_fixture (TCase * tc, SFun setup, SFun teardown) -{ - tcase_add_fixture (tc, setup, teardown, 1); -} - -static void -tcase_add_fixture (TCase * tc, SFun setup, SFun teardown, int ischecked) -{ - if (setup) { - if (ischecked) - check_list_add_end (tc->ch_sflst, fixture_create (setup, ischecked)); - else - check_list_add_end (tc->unch_sflst, fixture_create (setup, ischecked)); - } - - /* Add teardowns at front so they are run in reverse order. */ - if (teardown) { - if (ischecked) - check_list_add_front (tc->ch_tflst, fixture_create (teardown, ischecked)); - else - check_list_add_front (tc->unch_tflst, - fixture_create (teardown, ischecked)); - } -} - -void -tcase_set_timeout (TCase * tc, double timeout) -{ -#if defined(HAVE_FORK) - if (timeout >= 0) { - char *env = getenv ("CK_TIMEOUT_MULTIPLIER"); - - if (env != NULL) { - char *endptr = NULL; - double tmp = strtod (env, &endptr); - - if (tmp >= 0 && endptr != env && (*endptr) == '\0') { - timeout = timeout * tmp; - } - } - - tc->timeout.tv_sec = (time_t) floor (timeout); - tc->timeout.tv_nsec = - (long) ((timeout - floor (timeout)) * (double) NANOS_PER_SECONDS); - } -#else - (void) tc; - (void) timeout; - eprintf - ("This version does not support timeouts, as fork is not supported", - __FILE__, __LINE__); - /* Ignoring, as Check is not compiled with fork support. */ -#endif /* HAVE_FORK */ -} - -void -tcase_fn_start (const char *fname, const char *file, int line) -{ - send_ctx_info (CK_CTX_TEST); - send_loc_info (file, line); - - current_test_name = fname; -} - -const char * -tcase_name () -{ - return current_test_name; -} - -void -_mark_point (const char *file, int line) -{ - send_loc_info (file, line); -} - -void -_ck_assert_failed (const char *file, int line, const char *expr, ...) -{ - const char *msg; - va_list ap; - char buf[BUFSIZ]; - const char *to_send; - - send_loc_info (file, line); - - va_start (ap, expr); - msg = (const char *) va_arg (ap, char *); - - /* - * If a message was passed, format it with vsnprintf. - * Otherwise, print the expression as is. - */ - if (msg != NULL) { - vsnprintf (buf, BUFSIZ, msg, ap); - to_send = buf; - } else { - to_send = expr; - } - - va_end (ap); - send_failure_info (to_send); -#if defined(HAVE_FORK) && HAVE_FORK==1 - if (cur_fork_status () == CK_FORK) { - g_thread_pool_stop_unused_threads (); - _exit (1); - } -#endif /* HAVE_FORK */ - longjmp (error_jmp_buffer, 1); -} - -SRunner * -srunner_create (Suite * s) -{ - SRunner *sr = (SRunner *) emalloc (sizeof (SRunner)); /* freed in srunner_free */ - - sr->slst = check_list_create (); - if (s != NULL) - check_list_add_end (sr->slst, s); - sr->stats = (TestStats *) emalloc (sizeof (TestStats)); /* freed in srunner_free */ - sr->stats->n_checked = sr->stats->n_failed = sr->stats->n_errors = 0; - sr->resultlst = check_list_create (); - sr->log_fname = NULL; - sr->xml_fname = NULL; - sr->tap_fname = NULL; - sr->loglst = NULL; - -#if defined(HAVE_FORK) - sr->fstat = CK_FORK_GETENV; -#else - /* - * Overriding the default of running tests in fork mode, - * as this system does not have fork() - */ - sr->fstat = CK_NOFORK; -#endif /* HAVE_FORK */ - - return sr; -} - -void -srunner_add_suite (SRunner * sr, Suite * s) -{ - if (s == NULL) - return; - - check_list_add_end (sr->slst, s); -} - -void -srunner_free (SRunner * sr) -{ - List *l; - TestResult *tr; - - if (sr == NULL) - return; - - free (sr->stats); - l = sr->slst; - for (check_list_front (l); !check_list_at_end (l); check_list_advance (l)) { - suite_free ((Suite *) check_list_val (l)); - } - check_list_free (sr->slst); - - l = sr->resultlst; - for (check_list_front (l); !check_list_at_end (l); check_list_advance (l)) { - tr = (TestResult *) check_list_val (l); - tr_free (tr); - } - check_list_free (sr->resultlst); - - free (sr); -} - -int -srunner_ntests_failed (SRunner * sr) -{ - return sr->stats->n_failed + sr->stats->n_errors; -} - -int -srunner_ntests_run (SRunner * sr) -{ - return sr->stats->n_checked; -} - -TestResult ** -srunner_failures (SRunner * sr) -{ - int i = 0; - TestResult **trarray; - List *rlst; - - trarray = - (TestResult **) emalloc (sizeof (trarray[0]) * - srunner_ntests_failed (sr)); - - rlst = sr->resultlst; - for (check_list_front (rlst); !check_list_at_end (rlst); - check_list_advance (rlst)) { - TestResult *tr = (TestResult *) check_list_val (rlst); - - if (non_pass (tr->rtype)) - trarray[i++] = tr; - - } - return trarray; -} - -TestResult ** -srunner_results (SRunner * sr) -{ - int i = 0; - TestResult **trarray; - List *rlst; - - trarray = - (TestResult **) emalloc (sizeof (trarray[0]) * srunner_ntests_run (sr)); - - rlst = sr->resultlst; - for (check_list_front (rlst); !check_list_at_end (rlst); - check_list_advance (rlst)) { - trarray[i++] = (TestResult *) check_list_val (rlst); - } - return trarray; -} - -static int -non_pass (int val) -{ - return val != CK_PASS; -} - -TestResult * -tr_create (void) -{ - TestResult *tr; - - tr = (TestResult *) emalloc (sizeof (TestResult)); - tr_init (tr); - return tr; -} - -static void -tr_init (TestResult * tr) -{ - tr->ctx = CK_CTX_INVALID; - tr->line = -1; - tr->rtype = CK_TEST_RESULT_INVALID; - tr->msg = NULL; - tr->file = NULL; - tr->tcname = NULL; - tr->tname = NULL; - tr->duration = -1; -} - -void -tr_free (TestResult * tr) -{ - free (tr->file); - free (tr->msg); - free (tr); -} - - -const char * -tr_msg (TestResult * tr) -{ - return tr->msg; -} - -int -tr_lno (TestResult * tr) -{ - return tr->line; -} - -const char * -tr_lfile (TestResult * tr) -{ - return tr->file; -} - -int -tr_rtype (TestResult * tr) -{ - return tr->rtype; -} - -enum ck_result_ctx -tr_ctx (TestResult * tr) -{ - return tr->ctx; -} - -const char * -tr_tcname (TestResult * tr) -{ - return tr->tcname; -} - -static enum fork_status _fstat = CK_FORK; - -void -set_fork_status (enum fork_status fstat) -{ - if (fstat == CK_FORK || fstat == CK_NOFORK || fstat == CK_FORK_GETENV) - _fstat = fstat; - else - eprintf ("Bad status in set_fork_status", __FILE__, __LINE__); -} - -enum fork_status -cur_fork_status (void) -{ - return _fstat; -} - -/** - * Not all systems support the same clockid_t's. This call checks - * if the CLOCK_MONOTONIC clockid_t is valid. If so, that is returned, - * otherwise, CLOCK_REALTIME is returned. - * - * The clockid_t that was found to work on the first call is - * cached for subsequent calls. - */ -clockid_t -check_get_clockid () -{ - static clockid_t clockid = -1; - - if (clockid == -1) { -/* - * Only check if we have librt available. Otherwise, the clockid - * will be ignored anyway, as the clock_gettime() and - * timer_create() functions will be re-implemented in libcompat. - * Worse, if librt and alarm() are unavailable, this check - * will result in an assert(0). - */ -#if defined(HAVE_POSIX_TIMERS) && defined(HAVE_MONOTONIC_CLOCK) - timer_t timerid; - - if (timer_create (CLOCK_MONOTONIC, NULL, &timerid) == 0) { - timer_delete (timerid); - clockid = CLOCK_MONOTONIC; - } else { - clockid = CLOCK_REALTIME; - } -#else - clockid = CLOCK_MONOTONIC; -#endif - } - - return clockid; -} diff --git a/libs/gst/check/libcheck/check.h.in b/libs/gst/check/libcheck/check.h.in deleted file mode 100644 index d52abc5c1a..0000000000 --- a/libs/gst/check/libcheck/check.h.in +++ /dev/null @@ -1,1328 +0,0 @@ -/*-*- mode:C; -*- */ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#ifndef CHECK_H -#define CHECK_H - -#include <stddef.h> -#include <string.h> - -/* - Macros and functions starting with _ (underscore) are internal and - may change without notice. You have been warned!. -*/ - - -#ifdef __cplusplus -#define CK_CPPSTART extern "C" { -#define CK_CPPEND } -CK_CPPSTART -#endif -#if defined(__GNUC__) && defined(__GNUC_MINOR__) -#define GCC_VERSION_AT_LEAST(major, minor) \ -((__GNUC__ > (major)) || \ - (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))) -#else -#define GCC_VERSION_AT_LEAST(major, minor) 0 -#endif -#if GCC_VERSION_AT_LEAST(2,95) -#define CK_ATTRIBUTE_UNUSED __attribute__ ((unused)) -#else -#define CK_ATTRIBUTE_UNUSED -#endif /* GCC 2.95 */ -#if GCC_VERSION_AT_LEAST(2,5) -#define CK_ATTRIBUTE_NORETURN __attribute__ ((noreturn)) -#else -#define CK_ATTRIBUTE_NORETURN -#endif /* GCC 2.5 */ -#include <sys/types.h> - -/* - * Used to create the linker script for hiding lib-local symbols. Shall - * be put directly in front of the exported symbol. - */ -#define CK_EXPORT - -/* - * Used for MSVC to create the export attribute - * CK_DLL_EXP is defined during the compilation of the library - * on the command line. - */ -#ifndef CK_DLL_EXP -# if defined(_MSC_VER) -# define CK_DLL_EXP __declspec(dllimport) -# else -# define CK_DLL_EXP extern -# endif -#endif - -/* check version numbers */ -#define CHECK_MAJOR_VERSION (@CHECK_MAJOR_VERSION@) -#define CHECK_MINOR_VERSION (@CHECK_MINOR_VERSION@) -#define CHECK_MICRO_VERSION (@CHECK_MICRO_VERSION@) -CK_DLL_EXP /*extern*/ int CK_EXPORT check_major_version; -CK_DLL_EXP /*extern*/ int CK_EXPORT check_minor_version; -CK_DLL_EXP /*extern*/ int CK_EXPORT check_micro_version; - -#ifndef NULL -#define NULL ((void*)0) -#endif - -#if defined(_MSC_VER) -#define pid_t int -#endif - -/** - * Type for a test case - * - * A TCase represents a test case. Create with tcase_create, free - * with tcase_free. For the moment, test cases can only be run - * through a suite -*/ -typedef struct TCase TCase; - -/** - * Type for a test function - */ -typedef void (*TFun) (int); - -/** - * Type for a setup/teardown function - */ -typedef void (*SFun) (void); - -/** - * Type for a test suite - */ -typedef struct Suite Suite; - -/** - * Creates a test suite with the given name. - * - * Create a suite, which will contain test cases. Once - * created, use suite_add_tcase() to add test cases. - * When finished, create a suite runner from the - * suite using srunner_create() - * - * @param name name of the suite - * - * @return suite - * - * @since 0.6.0 - */ -CK_DLL_EXP Suite *CK_EXPORT suite_create (const char *name); - -/** - * Determines whether a given test suite contains a case named after a - * given string. - * - * @param s suite to check - * @param tcname test case to look for - * - * @return 1 iff the given test case is within the given suite; - * 0 otherwise - * - * @since 0.9.9 - */ -CK_DLL_EXP int CK_EXPORT suite_tcase (Suite * s, const char *tcname); - -/** - * Add a test case to a suite. - * - * Note that if the TCase has already been added attempting - * to add it again will be ignored. - * - * @param s suite to add test case to - * @param tc test case to add to suite - * - * @since 0.6.0 - */ -CK_DLL_EXP void CK_EXPORT suite_add_tcase (Suite * s, TCase * tc); - -/** - * Create a test case. - * - * Once created, tests can be added with the tcase_add_test() - * function, and the test case assigned to a suite with the - * suite_add_tcase() function. - * - * @param name name of the test case - * - * @return test case containing no tests - * - * @since 0.6.0 - * */ -CK_DLL_EXP TCase *CK_EXPORT tcase_create (const char *name); - -/** - * Associate a test case with certain tags. - * Replaces any existing tags with the new set. - * - * @param tc the test case - * - * @param tags string containing arbitrary tags separated by spaces. - * This will be copied. Passing NULL clears all tags. - * - * @since 0.11.0 - * */ -CK_DLL_EXP void CK_EXPORT tcase_set_tags (TCase * tc, const char *tags); -/** - * Add a test function to a test case - * - * @param tc test case to add test to - * @param tf test function to add to test case - * - * @since 0.6.0 - * */ -#define tcase_add_test(tc,tf) tcase_add_test_raise_signal(tc,tf,0) - -/** - * Add a test function with signal handling to a test case - * - * The added test is expected to terminate by throwing the given signal - * - * @param tc test case to add test to - * @param tf test function to add to test case - * @param signal expected signal for test function to throw in order for - * the test to be considered passing - * - * @since 0.9.2 - * */ -#define tcase_add_test_raise_signal(tc,tf,signal) \ - _tcase_add_test((tc),(tf),"" # tf "",(signal), 0, 0, 1) - -/** - * Add a test function with an expected exit value to a test case - * - * The added test is expected to terminate by exiting with the given value - * - * @param tc test case to add test to - * @param tf test function to add to test case - * @param expected_exit_value exit value for test function to return in - * order for the test to be considered passing - * - * @since 0.9.7 - */ -#define tcase_add_exit_test(tc, tf, expected_exit_value) \ - _tcase_add_test((tc),(tf),"" # tf "",0,(expected_exit_value),0,1) - -/** - * Add a looping test function to a test case - * - * The test will be called in a for(i = s; i < e; i++) loop with each - * iteration being executed in a new context. The loop variable 'i' is - * available in the test. - * - * @param tc test case to add test to - * @param tf function to add to test case - * @param s starting index for value "i" in test - * @param e ending index for value "i" in test - * - * @since 0.9.4 - */ -#define tcase_add_loop_test(tc,tf,s,e) \ - _tcase_add_test((tc),(tf),"" # tf "",0,0,(s),(e)) - -/** - * Add a looping test function with signal handling to a test case - * - * The test will be called in a for(i = s; i < e; i++) loop with each - * iteration being executed in a new context. The loop variable 'i' is - * available in the test. - * - * The added test is expected to terminate by throwing the given signal - * - * @param tc test case to add test to - * @param tf function to add to test case - * @param signal expected signal for test function to throw in order for - * the test to be considered passing - * @param s starting index for value "i" in test - * @param e ending index for value "i" in test - * - * @since 0.9.5 - */ -#define tcase_add_loop_test_raise_signal(tc,tf,signal,s,e) \ - _tcase_add_test((tc),(tf),"" # tf "",(signal),0,(s),(e)) - -/** - * Add a looping test function with an expected exit value to a test case - * - * The test will be called in a for(i = s; i < e; i++) loop with each - * iteration being executed in a new context. The loop variable 'i' is - * available in the test. - * - * The added test is expected to terminate by exiting with the given value - * - * @param tc test case to add test to - * @param tf function to add to test case - * @param expected_exit_value exit value for test function to return in - * order for the test to be considered passing - * @param s starting index for value "i" in test - * @param e ending index for value "i" in test - * - * @since 0.9.7 - */ -#define tcase_add_loop_exit_test(tc,tf,expected_exit_value,s,e) \ - _tcase_add_test((tc),(tf),"" # tf "",0,(expected_exit_value),(s),(e)) - -/* Add a test function to a test case - (function version -- use this when the macro won't work -*/ -CK_DLL_EXP void CK_EXPORT _tcase_add_test (TCase * tc, TFun tf, - const char *fname, int _signal, int allowed_exit_value, int start, int end); - -/** - * Add unchecked fixture setup/teardown functions to a test case - * - * Unchecked fixture functions are run at the start and end of the - * test case, and not before and after unit tests. Further, - * unchecked fixture functions are not run in a separate address space, - * like test functions, and so must not exit or signal (e.g., - * segfault). - * - * Also, when run in CK_NOFORK mode, unchecked fixture functions may - * lead to different unit test behavior if unit tests change data - * setup by the fixture functions. - * - * Note that if a setup function fails, the remaining setup functions - * will be omitted, as will the test case and the teardown functions. - * If a teardown function fails the remaining teardown functions will be - * omitted. - * - * @param tc test case to add unchecked fixture setup/teardown to - * @param setup function to add to be executed before the test case; - * if NULL no setup function is added - * @param teardown function to add to be executed after the test case; - * if NULL no teardown function is added - * @since 0.8.0 - */ -CK_DLL_EXP void CK_EXPORT tcase_add_unchecked_fixture (TCase * tc, SFun setup, - SFun teardown); - -/** - * Add checked fixture setup/teardown functions to a test case - * - * Checked fixture functions are run before and after each unit test inside - * of the address space of the test. Thus, if using CK_FORK - * mode the separate process running the unit test will survive signals - * or unexpected exits in the fixture function. Also, if the setup - * function is idempotent, unit test behavior will be the same in - * CK_FORK and CK_NOFORK modes. - * - * However, since fixture functions are run before and after each unit - * test, they should not be expensive code. - * - * Note that if a setup function fails, the remaining setup functions - * will be omitted, as will the test and the teardown functions. If a - * teardown function fails the remaining teardown functions will be - * omitted. - * - * @param tc test case to add checked fixture setup/teardown to - * @param setup function to add to be executed before each unit test in - * the test case; if NULL no setup function is added - * @param teardown function to add to be executed after each unit test in - * the test case; if NULL no teardown function is added - * - * @since 0.8.0 -*/ -CK_DLL_EXP void CK_EXPORT tcase_add_checked_fixture (TCase * tc, SFun setup, - SFun teardown); - -/** - * Set the timeout for all tests in a test case. - * - * A test that lasts longer than the timeout (in seconds) will be killed - * and thus fail with an error. - * - * If not set, the default timeout is one assigned at compile time. If - * the environment variable CK_DEFAULT_TIMEOUT is defined and no timeout - * is set, the value in the environment variable is used. - * - * If Check is compile without fork() support this call is ignored, - * as timeouts are not possible. - * - * @param tc test case to assign timeout to - * @param timeout to use, in seconds. If the value contains a decimal - * portion, but no high resolution timer is available, - * the value is rounded up to the nearest second. - * - * @since 0.9.2 - */ -CK_DLL_EXP void CK_EXPORT tcase_set_timeout (TCase * tc, double timeout); - -/* Internal function to mark the start of a test function */ -CK_DLL_EXP void CK_EXPORT tcase_fn_start (const char *fname, const char *file, - int line); - -/** - * Start a unit test with START_TEST(unit_name), end with END_TEST. - * - * One must use braces within a START_/END_ pair to declare new variables - * - * @since 0.6.0 - */ -#define START_TEST(__testname)\ -static void __testname (int _i CK_ATTRIBUTE_UNUSED)\ -{\ - tcase_fn_start (""# __testname, __FILE__, __LINE__); - -/** - * End a unit test - * - * @since 0.6.0 - */ -#define END_TEST } - -/* - * Fail the test case unless expr is false - * - * This call is deprecated. - */ -#define fail_unless ck_assert_msg - -/* - * Fail the test case if expr is false - * - * This call is deprecated. - * - * NOTE: The space before the comma sign before ## is essential to be compatible - * with gcc 2.95.3 and earlier. - * FIXME: these macros may conflict with C89 if expr is - * FIXME: strcmp (str1, str2) due to excessive string length. - */ -#define fail_if(expr, ...)\ - (expr) ? \ - _ck_assert_failed(__FILE__, __LINE__, "Failure '"#expr"' occurred" , ## __VA_ARGS__, NULL) \ - : _mark_point(__FILE__, __LINE__) - -/* - * Fail the test - * - * This call is deprecated. - */ -#define fail ck_abort_msg - -/* - * This is called whenever an assertion fails. - */ -CK_DLL_EXP void CK_EXPORT -_ck_assert_failed (const char *file, int line, const char *expr, ...) - CK_ATTRIBUTE_NORETURN; - -/** - * Fail the test if expression is false - * - * @param expr expression to evaluate - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.6 - */ -#define ck_assert(expr) ck_assert_msg(expr, NULL) - -/* The space before the comma sign before ## is essential to be compatible - with gcc 2.95.3 and earlier. -*/ -/** - * Fail the test if the expression is false; print message on failure - * - * @param expr expression to evaluate - * @param ... message to print (in printf format) if expression is false - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.6 - */ -#define ck_assert_msg(expr, ...) \ - (expr) ? \ - _mark_point(__FILE__, __LINE__) : \ - _ck_assert_failed(__FILE__, __LINE__, "Assertion '"#expr"' failed" , ## __VA_ARGS__, NULL) - -/** - * Unconditionally fail the test - * - * @note Once called, the remaining of the test is aborted - * - * @since 0.9.6 - */ -#define ck_abort() ck_abort_msg(NULL) -/** - * Unconditionally fail the test; print a message - * - * @param ... message to print (in printf format) - * - * @note Once called, the remaining of the test is aborted - * - * @since 0.9.6 - */ -#define ck_abort_msg(...) _ck_assert_failed(__FILE__, __LINE__, "Failed" , ## __VA_ARGS__, NULL) - -/* Signed and unsigned integer comparison macros with improved output compared to ck_assert(). */ -/* OP may be any comparison operator. */ -#define _ck_assert_int(X, OP, Y) do { \ - gint64 _ck_x = (X); \ - gint64 _ck_y = (Y); \ - ck_assert_msg(_ck_x OP _ck_y, "Assertion '%s' failed: " \ - "%s==%" G_GINT64_FORMAT ", %s==%" G_GINT64_FORMAT, #X#OP#Y, #X, _ck_x, #Y, _ck_y); \ -} while (0) - -/** - * Check two signed integers to determine if X==Y - * - * If not X==Y, the test fails. - * - * @param X signed integer - * @param Y signed integer to compare against X - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.6 - */ -#define ck_assert_int_eq(X, Y) _ck_assert_int(X, ==, Y) -/** - * Check two signed integers to determine if X!=Y - * - * If not X!=Y, the test fails. - * - * @param X signed integer - * @param Y signed integer to compare against X - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.6 - */ -#define ck_assert_int_ne(X, Y) _ck_assert_int(X, !=, Y) -/** - * Check two signed integers to determine if X<Y - * - * If not X<Y, the test fails. - * - * @param X signed integer - * @param Y signed integer to compare against X - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.10 - */ -#define ck_assert_int_lt(X, Y) _ck_assert_int(X, <, Y) -/** - * Check two signed integers to determine if X<=Y - * - * If not X<=Y, the test fails. - * - * @param X signed integer - * @param Y signed integer to compare against X - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.10 - */ -#define ck_assert_int_le(X, Y) _ck_assert_int(X, <=, Y) -/** - * Check two signed integers to determine if X>Y - * - * If not X>Y, the test fails. - * - * @param X signed integer - * @param Y signed integer to compare against X - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.10 - */ -#define ck_assert_int_gt(X, Y) _ck_assert_int(X, >, Y) -/** - * Check two signed integers to determine if X>=Y - * - * If not X>=Y, the test fails. - * - * @param X signed integer - * @param Y signed integer to compare against X - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.10 - */ -#define ck_assert_int_ge(X, Y) _ck_assert_int(X, >=, Y) - -#define _ck_assert_uint(X, OP, Y) do { \ - guint64 _ck_x = (X); \ - guint64 _ck_y = (Y); \ - ck_assert_msg(_ck_x OP _ck_y, "Assertion '%s' failed: " \ - "%s==%" G_GUINT64_FORMAT ", %s==%" G_GUINT64_FORMAT, #X#OP#Y, #X, _ck_x, #Y, _ck_y); \ -} while (0) -/** - * Check two unsigned integers to determine if X==Y - * - * If not X==Y, the test fails. - * - * @param X signed integer - * @param Y signed integer to compare against X - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.10 - */ -#define ck_assert_uint_eq(X, Y) _ck_assert_uint(X, ==, Y) -/** - * Check two unsigned integers to determine if X!=Y - * - * If not X!=Y, the test fails. - * - * @param X signed integer - * @param Y signed integer to compare against X - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.10 - */ -#define ck_assert_uint_ne(X, Y) _ck_assert_uint(X, !=, Y) -/** - * Check two unsigned integers to determine if X<Y - * - * If not X<Y, the test fails. - * - * @param X signed integer - * @param Y signed integer to compare against X - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.10 - */ -#define ck_assert_uint_lt(X, Y) _ck_assert_uint(X, <, Y) -/** - * Check two unsigned integers to determine if X<=Y - * - * If not X<=Y, the test fails. - * - * @param X signed integer - * @param Y signed integer to compare against X - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.10 - */ -#define ck_assert_uint_le(X, Y) _ck_assert_uint(X, <=, Y) -/** - * Check two unsigned integers to determine if X>Y - * - * If not X>Y, the test fails. - * - * @param X signed integer - * @param Y signed integer to compare against X - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.10 - */ -#define ck_assert_uint_gt(X, Y) _ck_assert_uint(X, >, Y) -/** - * Check two unsigned integers to determine if X>=Y - * - * If not X>=Y, the test fails. - * - * @param X signed integer - * @param Y signed integer to compare against X - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.10 - */ -#define ck_assert_uint_ge(X, Y) _ck_assert_uint(X, >=, Y) - -/* String comparison macros with improved output compared to ck_assert() */ -/* OP might be any operator that can be used in '0 OP strcmp(X,Y)' comparison */ -/* The x and y parameter swap in strcmp() is needed to handle >, >=, <, <= operators */ -#define _ck_assert_str(X, OP, Y) do { \ - const char* _ck_x = (X); \ - const char* _ck_y = (Y); \ - ck_assert_msg(0 OP strcmp(_ck_y, _ck_x), \ - "Assertion '%s' failed: %s==\"%s\", %s==\"%s\"", #X#OP#Y, #X, _ck_x, #Y, _ck_y); \ -} while (0) -/** - * Check two strings to determine if 0==strcmp(X,Y) - * - * If not 0==strcmp(X,Y), the test fails. - * - * @param X string - * @param Y string to compare against X - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.6 - */ -#define ck_assert_str_eq(X, Y) _ck_assert_str(X, ==, Y) -/** - * Check two strings to determine if 0!=strcmp(X,Y) - * - * If not 0!=strcmp(X,Y), the test fails. - * - * @param X string - * @param Y string to compare against X - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.6 - */ -#define ck_assert_str_ne(X, Y) _ck_assert_str(X, !=, Y) -/** - * Check two strings to determine if 0<strcmp(X,Y), (e.g. strcmp(X,Y)>0) - * - * If not 0<strcmp(X,Y), the test fails. - * - * @param X string - * @param Y string to compare against X - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.10 - */ -#define ck_assert_str_lt(X, Y) _ck_assert_str(X, <, Y) -/** - * Check two strings to determine if 0<=strcmp(X,Y) (e.g. strcmp(X,Y)>=0) - * - * If not 0<=strcmp(X,Y), the test fails. - * - * @param X string - * @param Y string to compare against X - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.10 - */ -#define ck_assert_str_le(X, Y) _ck_assert_str(X, <=, Y) -/** - * Check two strings to determine if 0<strcmp(X,Y) (e.g. strcmp(X,Y)>0) - * - * If not 0<strcmp(X,Y), the test fails. - * - * @param X string - * @param Y string to compare against X - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.10 - */ -#define ck_assert_str_gt(X, Y) _ck_assert_str(X, >, Y) -/** - * Check two strings to determine if 0>=strcmp(X,Y) (e.g. strcmp(X,Y)<=0) - * - * If not 0>=strcmp(X,Y), the test fails. - * - * @param X string - * @param Y string to compare against X - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.10 - */ -#define ck_assert_str_ge(X, Y) _ck_assert_str(X, >=, Y) - -/* Pointer comparison macros with improved output compared to ck_assert(). */ -/* OP may only be == or != */ -#define _ck_assert_ptr(X, OP, Y) do { \ - const void* _ck_x = (X); \ - const void* _ck_y = (Y); \ - ck_assert_msg(_ck_x OP _ck_y, "Assertion '%s' failed: %s==%#x, %s==%#x", #X#OP#Y, #X, _ck_x, #Y, _ck_y); \ -} while (0) - -/** - * Check if two pointers are equal. - * - * If the two passed pointers are not equal, the test - * fails. - * - * @param X pointer - * @param Y pointer to compare against X - * - * @note If the check fails, the remaining of the test is aborted - * - * @since 0.9.10 - */ -#define ck_assert_ptr_eq(X, Y) _ck_assert_ptr(X, ==, Y) - -/** - * Check if two pointers are not. - * - * If the two passed pointers are equal, the test fails. - * - * @param X pointer - * @param Y pointer to compare against X - * - * @since 0.9.10 - */ -#define ck_assert_ptr_ne(X, Y) _ck_assert_ptr(X, !=, Y) - -/** - * Mark the last point reached in a unit test. - * - * If the test throws a signal or exits, the location noted with the - * failure is the last location of a ck_assert*() or ck_abort() call. - * Use mark_point() to record intermediate locations (useful for tracking down - * crashes or exits). - * - * @since 0.6.0 -*/ -#define mark_point() _mark_point(__FILE__,__LINE__) - -/* Non macro version of #mark_point */ -CK_DLL_EXP void CK_EXPORT _mark_point (const char *file, int line); - -/** - * Enum describing the possible results of a test - */ -enum test_result -{ - CK_TEST_RESULT_INVALID, /**< Default value; should not encounter this */ - CK_PASS, /**< Test passed */ - CK_FAILURE, /**< Test completed but failed */ - CK_ERROR /**< Test failed to complete - (unexpected signal or non-zero early exit) */ -}; - -/** - * Enum specifying the verbosity of output a SRunner should produce - */ -enum print_output -{ - CK_SILENT, /**< No output */ - CK_MINIMAL, /**< Only summary output */ - CK_NORMAL, /**< All failed tests */ - CK_VERBOSE, /**< All tests */ - CK_ENV, /**< Look at environment var CK_VERBOSITY - for what verbosity to use, which can be - either "silent", "minimal", "normal", - or "verbose". If the environment variable - is not set, then CK_NORMAL will be used.*/ -#if @ENABLE_SUBUNIT@ - CK_SUBUNIT, /**< Run as a subunit child process */ -#endif - CK_LAST /**< Not a valid option */ -}; - -/** - * Holds state for a running of a test suite - */ -typedef struct SRunner SRunner; - -/** - * Opaque type for a test failure - */ -typedef struct TestResult TestResult; - -/** - * Enum representing the types of contexts for a test - */ -enum ck_result_ctx -{ - CK_CTX_INVALID, /**< Default value; should not encounter this */ - CK_CTX_SETUP, /**< Setup before a test */ - CK_CTX_TEST, /**< Body of test itself */ - CK_CTX_TEARDOWN /**< Teardown after a test */ -}; - -/** - * Retrieve type of result that the given test result represents. - * - * This is a member of test_result, and can represent a - * pass, failure, or error. - * - * @param tr test result to retrieve result from - * - * @return result of given test - * - * @since 0.6.0 - */ -CK_DLL_EXP int CK_EXPORT tr_rtype (TestResult * tr); - -/** - * Retrieve context in which the result occurred for the given test result. - * - * The types of contents include the test setup, teardown, or the - * body of the test itself. - * - * @param tr test result to retrieve context from - * - * @return context to which the given test result applies - * - * @since 0.8.0 - */ -CK_DLL_EXP enum ck_result_ctx CK_EXPORT tr_ctx (TestResult * tr); - -/** - * Retrieve failure message from test result, if applicable. - * - * @return pointer to a message, if one exists. NULL otherwise. - * - * @since 0.6.0 - */ -CK_DLL_EXP const char *CK_EXPORT tr_msg (TestResult * tr); - -/** - * Retrieve line number at which a failure occurred, if applicable. - * - * @return If the test resulted in a failure, returns the line number - * that the failure occurred on; otherwise returns -1. - * - * @since 0.6.0 - */ -CK_DLL_EXP int CK_EXPORT tr_lno (TestResult * tr); - -/** - * Retrieve file name at which a failure occurred, if applicable. - * - * @return If the test resulted in a failure, returns a string - * containing the name of the file where the failure - * occurred; otherwise returns NULL. - * - * @since 0.6.0 - */ -CK_DLL_EXP const char *CK_EXPORT tr_lfile (TestResult * tr); - -/** - * Retrieve test case name in which a failure occurred, if applicable. - * - * @return If the test resulted in a failure, returns a string - * containing the name of the test suite where the failure - * occurred; otherwise returns NULL. - * - * @since 0.6.0 - */ -CK_DLL_EXP const char *CK_EXPORT tr_tcname (TestResult * tr); - -/** - * Creates a suite runner for the given suite. - * - * Once created, additional suites can be added to the - * suite runner using srunner_add_suite(), and the suite runner can be - * run with srunner_run_all(). Once finished, the suite runner - * must be freed with srunner_free(). - * - * @param s suite to generate a suite runner for - * - * @return suite runner for the given suite - * - * @since 0.6.0 - */ -CK_DLL_EXP SRunner *CK_EXPORT srunner_create (Suite * s); - -/** - * Add an additional suite to a suite runner. - * - * The first suite in a suite runner is always added in srunner_create(). - * This call adds additional suites to a suite runner. - * - * @param sr suite runner to add the given suite - * @param s suite to add to the given suite runner - * - * @since 0.7.0 - */ -CK_DLL_EXP void CK_EXPORT srunner_add_suite (SRunner * sr, Suite * s); - -/** - * Frees a suite runner, including all contained suite and test cases. - * - * This call is responsible for freeing all resources related to a - * suite runner and all contained suites and test cases. Suite and - * test cases need not be freed individually, as this call handles that. - * - * @param sr suite runner to free - * - * @since 0.6.0 - */ -CK_DLL_EXP void CK_EXPORT srunner_free (SRunner * sr); - -/** - * Runs a suite runner and all contained suite, printing results to - * stdout as specified by the print_mode. - * - * In addition to running all suites, if the suite runner has been - * configured to output to a log, that is also performed. - * - * Note that if the CK_RUN_CASE, CK_RUN_SUITE, CK_INCLUDE_TAGS and/or - * CK_EXCLUDE_TAGS environment variables are defined, then only the - * named suites or test cases will run. - * - * @param sr suite runner to run all suites from - * @param print_mode the verbosity in which to report results to stdout - * - * @since 0.6.0 - */ -CK_DLL_EXP void CK_EXPORT srunner_run_all (SRunner * sr, - enum print_output print_mode); - -/** - * Run a specific suite or test case from a suite runner, printing results - * to stdout as specified by the print_mode. - * - * In addition to running any applicable suites or test cases, if the - * suite runner has been configured to output to a log, that is also - * performed. - * - * Note that if the sname and tcname parameters are passed as null - * then the function will fallback to using the environment variables - * CK_RUN_SUITE and CK_RUN_CASE respectively in order to select the - * suite/cases. - * - * Similarly if the CK_INCLUDE_TAGS and/or CK_EXCLUDE_TAGS environment - * variables are defined then these will further filter the test cases - * (see srunner_run_tagged, below). - * - * @param sr suite runner where the given suite or test case must be - * @param sname suite name to run. A NULL means use the value of the - * environment variable CK_RUN_SUITE if set, otherwise run "any/every - * suite". - * @param tcname test case name to run. A NULL means use the value of - * the environment variable CK_RUN_CASE if set, otherwise run - * "any/every case". - * @param print_mode the verbosity in which to report results to stdout - * - * @since 0.9.9 - */ -CK_DLL_EXP void CK_EXPORT srunner_run (SRunner * sr, const char *sname, - const char *tcname, enum print_output print_mode); - - -/** - * Run a specific suite or test case or testcases with specific tags - * from a suite runner, printing results to stdout as specified by the - * print_mode. - * - * In addition to running any applicable suites or test cases, if the - * suite runner has been configured to output to a log, that is also - * performed. - * - * Note that if sname, tcname, include_tags, exclude_tags parameters - * are passed as NULL then if the environment variables CK_RUN_SUITE, - * CK_RUN_CASE, CK_INCLUDE_TAGS, CK_EXCLUDE_TAGS are defined then these - * values will be used instead. - * - * @param sr suite runner where the given suite or test case must be - * @param sname suite name to run. A NULL means use the value of the - * environment variable CK_RUN_SUITE if set, otherwise run "any/every - * suite". - * @param tcname test case name to run. A NULL means use the value of - * the environment variable CK_RUN_CASE if set, otherwise run - * "any/every case". - * @param include_tags space separate list of tags. Only run test - * cases that share one of these tags. A NULL means use the value of - * the environment variable CK_INCLUDE_TAGS if set, otherwise run - * "any/every test case". - * @param exclude_tags space separate list of tags. Only run test - * cases that do not share one of these tags even if they are selected - * by an included tag. A NULL means use the value of the environment - * variable CK_EXCLUDE_TAGS if set, otherwise run "any/every test - * case". - * @param print_mode the verbosity in which to report results to stdout - * - * @since 0.11.0 - */ -CK_DLL_EXP void CK_EXPORT srunner_run_tagged (SRunner * sr, const char *sname, - const char *tcname, - const char *include_tags, - const char *exclude_tags, enum print_output print_mode); - -/** - * Retrieve the number of failed tests executed by a suite runner. - * - * This value represents both test failures and errors. - * - * @param sr suite runner to query for all failed tests - * - * @return number of test failures and errors found by the suite runner - * - * @since 0.6.1 - */ -CK_DLL_EXP int CK_EXPORT srunner_ntests_failed (SRunner * sr); - -/** - * Retrieve the total number of tests run by a suite runner. - * - * @param sr suite runner to query for all tests run - * - * @return number of all tests run by the suite runner - * - * @since 0.6.1 - */ -CK_DLL_EXP int CK_EXPORT srunner_ntests_run (SRunner * sr); - -/** - * Return an array of results for all failures found by a suite runner. - * - * Number of results is equal to srunner_nfailed_tests(). - * - * Information about individual results can be queried using: - * tr_rtype(), tr_ctx(), tr_msg(), tr_lno(), tr_lfile(), and tr_tcname(). - * - * Memory is malloc'ed and must be freed; however free the entire structure - * instead of individual test cases. - * - * @param sr suite runner to retrieve results from - * - * @return array of TestResult objects - * - * @since 0.6.0 - */ -CK_DLL_EXP TestResult **CK_EXPORT srunner_failures (SRunner * sr); - -/** - * Return an array of results for all tests run by a suite runner. - * - * Number of results is equal to srunner_ntests_run(), and excludes - * failures due to setup function failure. - * - * Information about individual results can be queried using: - * tr_rtype(), tr_ctx(), tr_msg(), tr_lno(), tr_lfile(), and tr_tcname(). - * - * Memory is malloc'ed and must be freed; however free the entire structure - * instead of individual test cases. - * - * @param sr suite runner to retrieve results from - * - * @return array of TestResult objects - * - * @since 0.6.1 -*/ -CK_DLL_EXP TestResult **CK_EXPORT srunner_results (SRunner * sr); - -/** - * Print the results contained in an SRunner to stdout. - * - * @param sr suite runner to print results for to stdout - * @param print_mode the print_output (verbosity) to use to report - * the result - * - * @since 0.7.0 - */ -CK_DLL_EXP void CK_EXPORT srunner_print (SRunner * sr, - enum print_output print_mode); - -/** - * Set the suite runner to output the result in log format to the - * given file. - * - * Note: log file setting is an initialize only operation -- it should - * be done immediately after SRunner creation, and the log file can't be - * changed after being set. - * - * This setting does not conflict with the other log output types; - * all logging types can occur concurrently if configured. - * - * @param sr suite runner to log results of in log format - * @param fname file name to output log results to - * - * @since 0.7.1 -*/ -CK_DLL_EXP void CK_EXPORT srunner_set_log (SRunner * sr, const char *fname); - -/** - * Checks if the suite runner is assigned a file for log output. - * - * @param sr suite runner to check - * - * @return 1 iff the suite runner currently is configured to output - * in log format; 0 otherwise - * - * @since 0.7.1 - */ -CK_DLL_EXP int CK_EXPORT srunner_has_log (SRunner * sr); - -/** - * Retrieves the name of the currently assigned file - * for log output, if any exists. - * - * @return the name of the log file, or NULL if none is configured - * - * @since 0.7.1 - */ -CK_DLL_EXP const char *CK_EXPORT srunner_log_fname (SRunner * sr); - -/** - * Set the suite runner to output the result in XML format to the - * given file. - * - * Note: XML file setting is an initialize only operation -- it should - * be done immediately after SRunner creation, and the XML file can't be - * changed after being set. - * - * This setting does not conflict with the other log output types; - * all logging types can occur concurrently if configured. - * - * @param sr suite runner to log results of in XML format - * @param fname file name to output XML results to - * - * @since 0.9.1 -*/ -CK_DLL_EXP void CK_EXPORT srunner_set_xml (SRunner * sr, const char *fname); - -/** - * Checks if the suite runner is assigned a file for XML output. - * - * @param sr suite runner to check - * - * @return 1 iff the suite runner currently is configured to output - * in XML format; 0 otherwise - * - * @since 0.9.1 - */ -CK_DLL_EXP int CK_EXPORT srunner_has_xml (SRunner * sr); - -/** - * Retrieves the name of the currently assigned file - * for XML output, if any exists. - * - * @return the name of the XML file, or NULL if none is configured - * - * @since 0.9.1 - */ -CK_DLL_EXP const char *CK_EXPORT srunner_xml_fname (SRunner * sr); - -/** - * Set the suite runner to output the result in TAP format to the - * given file. - * - * Note: TAP file setting is an initialize only operation -- it should - * be done immediately after SRunner creation, and the TAP file can't be - * changed after being set. - * - * This setting does not conflict with the other log output types; - * all logging types can occur concurrently if configured. - * - * @param sr suite runner to log results of in TAP format - * @param fname file name to output TAP results to - * - * @since 0.9.12 -*/ -CK_DLL_EXP void CK_EXPORT srunner_set_tap (SRunner * sr, const char *fname); - -/** - * Checks if the suite runner is assigned a file for TAP output. - * - * @param sr suite runner to check - * - * @return 1 iff the suite runner currently is configured to output - * in TAP format; 0 otherwise - * - * @since 0.9.12 - */ -CK_DLL_EXP int CK_EXPORT srunner_has_tap (SRunner * sr); - -/** - * Retrieves the name of the currently assigned file - * for TAP output, if any exists. - * - * @return the name of the TAP file, or NULL if none is configured - * - * @since 0.9.12 - */ -CK_DLL_EXP const char *CK_EXPORT srunner_tap_fname (SRunner * sr); - -/** - * Enum describing the current fork usage. - */ -enum fork_status -{ - CK_FORK_GETENV, /**< look in the environment for CK_FORK */ - CK_FORK, /**< call fork to run tests */ - CK_NOFORK /**< don't call fork */ -}; - -/** - * Retrieve the current fork status for the given suite runner - * - * @param sr suite runner to check fork status of - * - * @since 0.8.0 - */ -CK_DLL_EXP enum fork_status CK_EXPORT srunner_fork_status (SRunner * sr); - -/** - * Set the fork status for a given suite runner. - * - * The default fork status is CK_FORK_GETENV, which will look - * for the CK_FORK environment variable, which can be set to - * "yes" or "no". If the environment variable is not present, - * CK_FORK will be used if fork() is available on the system, - * otherwise CK_NOFORK is used. - * - * If set to CK_FORK or CK_NOFORK, the environment variable - * if defined is ignored. - * - * If Check is compiled without support for fork(), attempting - * to set the status to CK_FORK is ignored. - * - * @param sr suite runner to assign the fork status to - * @param fstat fork status to assign - * - * @since 0.8.0 - */ -CK_DLL_EXP void CK_EXPORT srunner_set_fork_status (SRunner * sr, - enum fork_status fstat); - -/** - * Invoke fork() during a test and assign the child to the same - * process group that the rest of the test case uses. - * - * One can invoke fork() directly during a test; however doing so - * may not guarantee that any children processes are destroyed once - * the test finishes. Once a test has completed, all processes in - * the process group will be killed; using this wrapper will prevent - * orphan processes. - * - * If Check is compiled without fork() support this call simply - * return -1 and does nothing. - * - * @return On success, the PID of the child process is returned in - * the parent, and 0 is returned in the child. On failure, - * a value of -1 is returned to the parent process and no - * child process is created. - * - * @since 0.9.3 - */ -#if !defined(_MSC_VER) -CK_DLL_EXP pid_t CK_EXPORT check_fork (void); -#endif - -/** - * Wait for the pid and exit. - * - * This is to be used in conjunction with check_fork(). When called, - * will wait for the given process to terminate. If the process - * exited without error, exit(EXIT_SUCCESS) is invoked; otherwise - * exit(EXIT_FAILURE) is invoked. - * - * If Check is compiled without support for fork(), this invokes - * exit(EXIT_FAILURE). - * - * @param pid process to wait for, created by check_fork() - * - * @since 0.9.3 - */ -#if !defined(_MSC_VER) -CK_DLL_EXP void CK_EXPORT -check_waitpid_and_exit (pid_t pid) - CK_ATTRIBUTE_NORETURN; -#endif - -#ifdef __cplusplus -CK_CPPEND -#endif -#endif /* CHECK_H */ diff --git a/libs/gst/check/libcheck/check_error.c b/libs/gst/check/libcheck/check_error.c deleted file mode 100644 index 8f293aa77e..0000000000 --- a/libs/gst/check/libcheck/check_error.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat/libcompat.h" - -#include <stdarg.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <errno.h> -#include <setjmp.h> - -#include "check_error.h" - -/** - * Storage for setjmp/longjmp context information used in NOFORK mode - */ -jmp_buf error_jmp_buffer; - - -/* FIXME: including a colon at the end is a bad way to indicate an error */ -void -eprintf (const char *fmt, const char *file, int line, ...) -{ - va_list args; - - fflush (stderr); - - fprintf (stderr, "%s:%d: ", file, line); - va_start (args, line); - vfprintf (stderr, fmt, args); - va_end (args); - - /*include system error information if format ends in colon */ - if (fmt[0] != '\0' && fmt[strlen (fmt) - 1] == ':') - fprintf (stderr, " %s", strerror (errno)); - fprintf (stderr, "\n"); - - exit (2); -} - -void * -emalloc (size_t n) -{ - void *p; - - p = malloc (n); - if (p == NULL) - eprintf ("malloc of %u bytes failed:", __FILE__, __LINE__ - 2, n); - return p; -} - -void * -erealloc (void *ptr, size_t n) -{ - void *p; - - p = realloc (ptr, n); - if (p == NULL) - eprintf ("realloc of %u bytes failed:", __FILE__, __LINE__ - 2, n); - return p; -} diff --git a/libs/gst/check/libcheck/check_error.h b/libs/gst/check/libcheck/check_error.h deleted file mode 100644 index f325fc69c9..0000000000 --- a/libs/gst/check/libcheck/check_error.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#ifndef ERROR_H -#define ERROR_H - -#include "libcompat/libcompat.h" -#include <setjmp.h> - -extern jmp_buf error_jmp_buffer; - -/* Include stdlib.h beforehand */ - -/* Print error message and die - If fmt ends in colon, include system error information */ -void -eprintf (const char *fmt, const char *file, int line, ...) - CK_ATTRIBUTE_NORETURN; -/* malloc or die */ - void *emalloc (size_t n); - void *erealloc (void *, size_t n); - -#endif /*ERROR_H */ diff --git a/libs/gst/check/libcheck/check_impl.h b/libs/gst/check/libcheck/check_impl.h deleted file mode 100644 index 420543e2ef..0000000000 --- a/libs/gst/check/libcheck/check_impl.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#ifndef CHECK_IMPL_H -#define CHECK_IMPL_H - -/* This header should be included by any module that needs - to know the implementation details of the check structures - Include stdio.h, time.h, & list.h before this header -*/ - -#define US_PER_SEC 1000000 -#define NANOS_PER_SECONDS 1000000000 - -/** calculate the difference in useconds out of two "struct timespec"s */ -#define DIFF_IN_USEC(begin, end) \ - ( (((end).tv_sec - (begin).tv_sec) * US_PER_SEC) + \ - ((end).tv_nsec/1000) - ((begin).tv_nsec/1000) ) - -typedef struct TF -{ - TFun fn; - int loop_start; - int loop_end; - const char *name; - int signal; - signed char allowed_exit_value; -} TF; - -struct Suite -{ - const char *name; - List *tclst; /* List of test cases */ -}; - -typedef struct Fixture -{ - int ischecked; - SFun fun; -} Fixture; - -struct TCase -{ - const char *name; - struct timespec timeout; - List *tflst; /* list of test functions */ - List *unch_sflst; - List *unch_tflst; - List *ch_sflst; - List *ch_tflst; - List *tags; -}; - -typedef struct TestStats -{ - int n_checked; - int n_failed; - int n_errors; -} TestStats; - -struct TestResult -{ - enum test_result rtype; /* Type of result */ - enum ck_result_ctx ctx; /* When the result occurred */ - char *file; /* File where the test occurred */ - int line; /* Line number where the test occurred */ - int iter; /* The iteration value for looping tests */ - int duration; /* duration of this test in microseconds */ - const char *tcname; /* Test case that generated the result */ - const char *tname; /* Test that generated the result */ - char *msg; /* Failure message */ -}; - -TestResult *tr_create (void); -void tr_reset (TestResult * tr); -void tr_free (TestResult * tr); - -enum cl_event -{ - CLINITLOG_SR, /* Initialize log file */ - CLENDLOG_SR, /* Tests are complete */ - CLSTART_SR, /* Suite runner start */ - CLSTART_S, /* Suite start */ - CLEND_SR, /* Suite runner end */ - CLEND_S, /* Suite end */ - CLSTART_T, /* A test case is about to run */ - CLEND_T /* Test case end */ -}; - -typedef void (*LFun) (SRunner *, FILE *, enum print_output, - void *, enum cl_event); - -typedef struct Log -{ - FILE *lfile; - LFun lfun; - int close; - enum print_output mode; -} Log; - -struct SRunner -{ - List *slst; /* List of Suite objects */ - TestStats *stats; /* Run statistics */ - List *resultlst; /* List of unit test results */ - const char *log_fname; /* name of log file */ - const char *xml_fname; /* name of xml output file */ - const char *tap_fname; /* name of tap output file */ - List *loglst; /* list of Log objects */ - enum fork_status fstat; /* controls if suites are forked or not - NOTE: Don't use this value directly, - instead use srunner_fork_status */ -}; - - -void set_fork_status (enum fork_status fstat); -enum fork_status cur_fork_status (void); - -clockid_t check_get_clockid (void); - -unsigned int tcase_matching_tag (TCase * tc, List * check_for); -List *tag_string_to_list (const char *tags_string); - -#endif /* CHECK_IMPL_H */ diff --git a/libs/gst/check/libcheck/check_list.c b/libs/gst/check/libcheck/check_list.c deleted file mode 100644 index 610a1250eb..0000000000 --- a/libs/gst/check/libcheck/check_list.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat/libcompat.h" - -#include <stdlib.h> -#include <string.h> - -#include "check_list.h" -#include "check_error.h" - - -enum -{ - LINIT = 1, - LGROW = 2 -}; - -struct List -{ - unsigned int n_elts; - unsigned int max_elts; - int current; /* pointer to the current node */ - int last; /* pointer to the node before END */ - void **data; -}; - -static void -maybe_grow (List * lp) -{ - if (lp->n_elts >= lp->max_elts) { - lp->max_elts *= LGROW; - lp->data = - (void **) erealloc (lp->data, lp->max_elts * sizeof (lp->data[0])); - } -} - -List * -check_list_create (void) -{ - List *lp; - - lp = (List *) emalloc (sizeof (List)); - lp->n_elts = 0; - lp->max_elts = LINIT; - lp->data = (void **) emalloc (sizeof (lp->data[0]) * LINIT); - lp->current = lp->last = -1; - return lp; -} - -void -check_list_add_front (List * lp, void *val) -{ - if (lp == NULL) - return; - maybe_grow (lp); - memmove (lp->data + 1, lp->data, lp->n_elts * sizeof lp->data[0]); - lp->last++; - lp->n_elts++; - lp->current = 0; - lp->data[lp->current] = val; -} - -void -check_list_add_end (List * lp, void *val) -{ - if (lp == NULL) - return; - maybe_grow (lp); - lp->last++; - lp->n_elts++; - lp->current = lp->last; - lp->data[lp->current] = val; -} - -int -check_list_at_end (List * lp) -{ - if (lp->current == -1) - return 1; - else - return (lp->current > lp->last); -} - -void -check_list_front (List * lp) -{ - if (lp->current == -1) - return; - lp->current = 0; -} - - -void -check_list_free (List * lp) -{ - if (lp == NULL) - return; - - free (lp->data); - free (lp); -} - -void * -check_list_val (List * lp) -{ - if (lp == NULL) - return NULL; - if (lp->current == -1 || lp->current > lp->last) - return NULL; - - return lp->data[lp->current]; -} - -void -check_list_advance (List * lp) -{ - if (lp == NULL) - return; - if (check_list_at_end (lp)) - return; - lp->current++; -} - - -void -check_list_apply (List * lp, void (*fp) (void *)) -{ - if (lp == NULL || fp == NULL) - return; - - for (check_list_front (lp); !check_list_at_end (lp); check_list_advance (lp)) - fp (check_list_val (lp)); - -} - -bool -check_list_contains (List * lp, void *val) -{ - for (check_list_front (lp); !check_list_at_end (lp); check_list_advance (lp)) { - if (check_list_val (lp) == val) { - return true; - } - } - - return false; -} diff --git a/libs/gst/check/libcheck/check_list.h b/libs/gst/check/libcheck/check_list.h deleted file mode 100644 index 005361e4ff..0000000000 --- a/libs/gst/check/libcheck/check_list.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#ifndef CHECK_LIST_H -#define CHECK_LIST_H - -#include <stdbool.h> - -typedef struct List List; - -/* Create an empty list */ -List *check_list_create (void); - -/* Is list at end? */ -int check_list_at_end (List * lp); - -/* Position list at front */ -void check_list_front (List * lp); - -/* Add a value to the front of the list, - positioning newly added value as current value. - More expensive than list_add_end, as it uses memmove. */ -void check_list_add_front (List * lp, void *val); - -/* Add a value to the end of the list, - positioning newly added value as current value */ -void check_list_add_end (List * lp, void *val); - -/* Give the value of the current node */ -void *check_list_val (List * lp); - -/* Position the list at the next node */ -void check_list_advance (List * lp); - -/* Free a list, but don't free values */ -void check_list_free (List * lp); - -void check_list_apply (List * lp, void (*fp) (void *)); - -/* Return true if the list contains the value, false otherwise */ -bool check_list_contains (List * lp, void *val); - - -#endif /* CHECK_LIST_H */ diff --git a/libs/gst/check/libcheck/check_log.c b/libs/gst/check/libcheck/check_log.c deleted file mode 100644 index bb81d6bd0c..0000000000 --- a/libs/gst/check/libcheck/check_log.c +++ /dev/null @@ -1,548 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat/libcompat.h" - -#include <stdlib.h> -#include <stdio.h> -#include <internal-check.h> -#if ENABLE_SUBUNIT -#include <subunit/child.h> -#endif - -#include "check_error.h" -#include "check_list.h" -#include "check_impl.h" -#include "check_log.h" -#include "check_print.h" -#include "check_str.h" - -/* - * If a log file is specified to be "-", then instead of - * opening a file the log output is printed to stdout. - */ -#define STDOUT_OVERRIDE_LOG_FILE_NAME "-" - -static void srunner_send_evt (SRunner * sr, void *obj, enum cl_event evt); - -void -srunner_set_log (SRunner * sr, const char *fname) -{ - if (sr->log_fname) - return; - sr->log_fname = fname; -} - -int -srunner_has_log (SRunner * sr) -{ - return srunner_log_fname (sr) != NULL; -} - -const char * -srunner_log_fname (SRunner * sr) -{ - /* check if log filename have been set explicitly */ - if (sr->log_fname != NULL) - return sr->log_fname; - - return getenv ("CK_LOG_FILE_NAME"); -} - - -void -srunner_set_xml (SRunner * sr, const char *fname) -{ - if (sr->xml_fname) - return; - sr->xml_fname = fname; -} - -int -srunner_has_xml (SRunner * sr) -{ - return srunner_xml_fname (sr) != NULL; -} - -const char * -srunner_xml_fname (SRunner * sr) -{ - /* check if XML log filename have been set explicitly */ - if (sr->xml_fname != NULL) { - return sr->xml_fname; - } - - return getenv ("CK_XML_LOG_FILE_NAME"); -} - -void -srunner_set_tap (SRunner * sr, const char *fname) -{ - if (sr->tap_fname) - return; - sr->tap_fname = fname; -} - -int -srunner_has_tap (SRunner * sr) -{ - return srunner_tap_fname (sr) != NULL; -} - -const char * -srunner_tap_fname (SRunner * sr) -{ - /* check if tap log filename have been set explicitly */ - if (sr->tap_fname != NULL) { - return sr->tap_fname; - } - - return getenv ("CK_TAP_LOG_FILE_NAME"); -} - -void -srunner_register_lfun (SRunner * sr, FILE * lfile, int close, - LFun lfun, enum print_output printmode) -{ - Log *l = (Log *) emalloc (sizeof (Log)); - - if (printmode == CK_ENV) { - printmode = get_env_printmode (); - } - - l->lfile = lfile; - l->lfun = lfun; - l->close = close; - l->mode = printmode; - check_list_add_end (sr->loglst, l); - return; -} - -void -log_srunner_start (SRunner * sr) -{ - srunner_send_evt (sr, NULL, CLSTART_SR); -} - -void -log_srunner_end (SRunner * sr) -{ - srunner_send_evt (sr, NULL, CLEND_SR); -} - -void -log_suite_start (SRunner * sr, Suite * s) -{ - srunner_send_evt (sr, s, CLSTART_S); -} - -void -log_suite_end (SRunner * sr, Suite * s) -{ - srunner_send_evt (sr, s, CLEND_S); -} - -void -log_test_start (SRunner * sr, TCase * tc, TF * tfun) -{ - char buffer[100]; - - snprintf (buffer, 99, "%s:%s", tc->name, tfun->name); - srunner_send_evt (sr, buffer, CLSTART_T); -} - -void -log_test_end (SRunner * sr, TestResult * tr) -{ - srunner_send_evt (sr, tr, CLEND_T); -} - -static void -srunner_send_evt (SRunner * sr, void *obj, enum cl_event evt) -{ - List *l; - Log *lg; - - l = sr->loglst; - for (check_list_front (l); !check_list_at_end (l); check_list_advance (l)) { - lg = (Log *) check_list_val (l); - fflush (lg->lfile); - lg->lfun (sr, lg->lfile, lg->mode, obj, evt); - fflush (lg->lfile); - } -} - -void -stdout_lfun (SRunner * sr, FILE * file, enum print_output printmode, - void *obj, enum cl_event evt) -{ - Suite *s; - - switch (evt) { - case CLINITLOG_SR: - break; - case CLENDLOG_SR: - break; - case CLSTART_SR: - if (printmode > CK_SILENT) { - fprintf (file, "Running suite(s):"); - } - break; - case CLSTART_S: - s = (Suite *) obj; - if (printmode > CK_SILENT) { - fprintf (file, " %s\n", s->name); - } - break; - case CLEND_SR: - if (printmode > CK_SILENT) { - /* we don't want a newline before printing here, newlines should - come after printing a string, not before. it's better to add - the newline above in CLSTART_S. - */ - srunner_fprint (file, sr, printmode); - } - break; - case CLEND_S: - break; - case CLSTART_T: - break; - case CLEND_T: - break; - default: - eprintf ("Bad event type received in stdout_lfun", __FILE__, __LINE__); - } - - -} - -void -lfile_lfun (SRunner * sr, FILE * file, - enum print_output printmode CK_ATTRIBUTE_UNUSED, void *obj, - enum cl_event evt) -{ - TestResult *tr; - Suite *s; - - switch (evt) { - case CLINITLOG_SR: - break; - case CLENDLOG_SR: - break; - case CLSTART_SR: - break; - case CLSTART_S: - s = (Suite *) obj; - fprintf (file, "Running suite %s\n", s->name); - break; - case CLEND_SR: - fprintf (file, "Results for all suites run:\n"); - srunner_fprint (file, sr, CK_MINIMAL); - break; - case CLEND_S: - break; - case CLSTART_T: - break; - case CLEND_T: - tr = (TestResult *) obj; - tr_fprint (file, tr, CK_VERBOSE); - break; - default: - eprintf ("Bad event type received in lfile_lfun", __FILE__, __LINE__); - } - - -} - -void -xml_lfun (SRunner * sr CK_ATTRIBUTE_UNUSED, FILE * file, - enum print_output printmode CK_ATTRIBUTE_UNUSED, void *obj, - enum cl_event evt) -{ - TestResult *tr; - Suite *s; - static struct timespec ts_start = { 0, 0 }; - static char t[sizeof "yyyy-mm-dd hh:mm:ss"] = { 0 }; - - if (t[0] == 0) { - struct timeval inittv; - struct tm now; - - gettimeofday (&inittv, NULL); - clock_gettime (check_get_clockid (), &ts_start); - if (localtime_r ((const time_t *) &(inittv.tv_sec), &now) != NULL) { - strftime (t, sizeof ("yyyy-mm-dd hh:mm:ss"), "%Y-%m-%d %H:%M:%S", &now); - } - } - - switch (evt) { - case CLINITLOG_SR: - fprintf (file, "<?xml version=\"1.0\"?>\n"); - fprintf (file, - "<?xml-stylesheet type=\"text/xsl\" href=\"http://check.sourceforge.net/xml/check_unittest.xslt\"?>\n"); - fprintf (file, - "<testsuites xmlns=\"http://check.sourceforge.net/ns\">\n"); - fprintf (file, " <datetime>%s</datetime>\n", t); - break; - case CLENDLOG_SR: - { - struct timespec ts_end = { 0, 0 }; - unsigned long duration; - - /* calculate time the test were running */ - clock_gettime (check_get_clockid (), &ts_end); - duration = (unsigned long) DIFF_IN_USEC (ts_start, ts_end); - fprintf (file, " <duration>%lu.%06lu</duration>\n", - duration / US_PER_SEC, duration % US_PER_SEC); - fprintf (file, "</testsuites>\n"); - } - break; - case CLSTART_SR: - break; - case CLSTART_S: - s = (Suite *) obj; - fprintf (file, " <suite>\n"); - fprintf (file, " <title>"); - fprint_xml_esc (file, s->name); - fprintf (file, "</title>\n"); - break; - case CLEND_SR: - break; - case CLEND_S: - fprintf (file, " </suite>\n"); - break; - case CLSTART_T: - break; - case CLEND_T: - tr = (TestResult *) obj; - tr_xmlprint (file, tr, CK_VERBOSE); - break; - default: - eprintf ("Bad event type received in xml_lfun", __FILE__, __LINE__); - } - -} - -void -tap_lfun (SRunner * sr CK_ATTRIBUTE_UNUSED, FILE * file, - enum print_output printmode CK_ATTRIBUTE_UNUSED, void *obj, - enum cl_event evt) -{ - TestResult *tr; - - static int num_tests_run = 0; - - switch (evt) { - case CLINITLOG_SR: - /* As this is a new log file, reset the number of tests executed */ - num_tests_run = 0; - break; - case CLENDLOG_SR: - /* Output the test plan as the last line */ - fprintf (file, "1..%d\n", num_tests_run); - fflush (file); - break; - case CLSTART_SR: - break; - case CLSTART_S: - break; - case CLEND_SR: - break; - case CLEND_S: - break; - case CLSTART_T: - break; - case CLEND_T: - /* Print the test result to the tap file */ - num_tests_run += 1; - tr = (TestResult *) obj; - fprintf (file, "%s %d - %s:%s:%s: %s\n", - tr->rtype == CK_PASS ? "ok" : "not ok", num_tests_run, - tr->file, tr->tcname, tr->tname, tr->msg); - fflush (file); - break; - default: - eprintf ("Bad event type received in tap_lfun", __FILE__, __LINE__); - } -} - -#if ENABLE_SUBUNIT -void -subunit_lfun (SRunner * sr, FILE * file, enum print_output printmode, - void *obj, enum cl_event evt) -{ - TestResult *tr; - char const *name; - - /* assert(printmode == CK_SUBUNIT); */ - - switch (evt) { - case CLINITLOG_SR: - break; - case CLENDLOG_SR: - break; - case CLSTART_SR: - break; - case CLSTART_S: - break; - case CLEND_SR: - if (printmode > CK_SILENT) { - fprintf (file, "\n"); - srunner_fprint (file, sr, printmode); - } - break; - case CLEND_S: - break; - case CLSTART_T: - name = (const char *) obj; - subunit_test_start (name); - break; - case CLEND_T: - tr = (TestResult *) obj; - { - char *name = ck_strdup_printf ("%s:%s", tr->tcname, tr->tname); - char *msg = tr_short_str (tr); - - switch (tr->rtype) { - case CK_PASS: - subunit_test_pass (name); - break; - case CK_FAILURE: - subunit_test_fail (name, msg); - break; - case CK_ERROR: - subunit_test_error (name, msg); - break; - case CK_TEST_RESULT_INVALID: - default: - eprintf ("Bad result type in subunit_lfun", __FILE__, __LINE__); - free (name); - free (msg); - } - } - break; - default: - eprintf ("Bad event type received in subunit_lfun", __FILE__, __LINE__); - } -} -#endif - -static FILE * -srunner_open_file (const char *filename) -{ - FILE *f = NULL; - - if (strcmp (filename, STDOUT_OVERRIDE_LOG_FILE_NAME) == 0) { - f = stdout; - } else { - f = fopen (filename, "w"); - if (f == NULL) { - eprintf ("Error in call to fopen while opening file %s:", __FILE__, - __LINE__ - 2, filename); - } - } - return f; -} - -FILE * -srunner_open_lfile (SRunner * sr) -{ - FILE *f = NULL; - - if (srunner_has_log (sr)) { - f = srunner_open_file (srunner_log_fname (sr)); - } - return f; -} - -FILE * -srunner_open_xmlfile (SRunner * sr) -{ - FILE *f = NULL; - - if (srunner_has_xml (sr)) { - f = srunner_open_file (srunner_xml_fname (sr)); - } - return f; -} - -FILE * -srunner_open_tapfile (SRunner * sr) -{ - FILE *f = NULL; - - if (srunner_has_tap (sr)) { - f = srunner_open_file (srunner_tap_fname (sr)); - } - return f; -} - -void -srunner_init_logging (SRunner * sr, enum print_output print_mode) -{ - FILE *f; - - sr->loglst = check_list_create (); -#if ENABLE_SUBUNIT - if (print_mode != CK_SUBUNIT) -#endif - srunner_register_lfun (sr, stdout, 0, stdout_lfun, print_mode); -#if ENABLE_SUBUNIT - else - srunner_register_lfun (sr, stdout, 0, subunit_lfun, print_mode); -#endif - f = srunner_open_lfile (sr); - if (f) { - srunner_register_lfun (sr, f, f != stdout, lfile_lfun, print_mode); - } - f = srunner_open_xmlfile (sr); - if (f) { - srunner_register_lfun (sr, f, f != stdout, xml_lfun, print_mode); - } - f = srunner_open_tapfile (sr); - if (f) { - srunner_register_lfun (sr, f, f != stdout, tap_lfun, print_mode); - } - srunner_send_evt (sr, NULL, CLINITLOG_SR); -} - -void -srunner_end_logging (SRunner * sr) -{ - List *l; - int rval; - - srunner_send_evt (sr, NULL, CLENDLOG_SR); - - l = sr->loglst; - for (check_list_front (l); !check_list_at_end (l); check_list_advance (l)) { - Log *lg = (Log *) check_list_val (l); - - if (lg->close) { - rval = fclose (lg->lfile); - if (rval != 0) - eprintf ("Error in call to fclose while closing log file:", - __FILE__, __LINE__ - 2); - } - free (lg); - } - check_list_free (l); - sr->loglst = NULL; -} diff --git a/libs/gst/check/libcheck/check_log.h b/libs/gst/check/libcheck/check_log.h deleted file mode 100644 index c1936f973f..0000000000 --- a/libs/gst/check/libcheck/check_log.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#ifndef CHECK_LOG_H -#define CHECK_LOG_H - -void log_srunner_start (SRunner * sr); -void log_srunner_end (SRunner * sr); -void log_suite_start (SRunner * sr, Suite * s); -void log_suite_end (SRunner * sr, Suite * s); -void log_test_end (SRunner * sr, TestResult * tr); -void log_test_start (SRunner * sr, TCase * tc, TF * tfun); - -void stdout_lfun (SRunner * sr, FILE * file, enum print_output, - void *obj, enum cl_event evt); - -void lfile_lfun (SRunner * sr, FILE * file, enum print_output, - void *obj, enum cl_event evt); - -void xml_lfun (SRunner * sr, FILE * file, enum print_output, - void *obj, enum cl_event evt); - -void tap_lfun (SRunner * sr, FILE * file, enum print_output, - void *obj, enum cl_event evt); - -void subunit_lfun (SRunner * sr, FILE * file, enum print_output, - void *obj, enum cl_event evt); - -void srunner_register_lfun (SRunner * sr, FILE * lfile, int close, - LFun lfun, enum print_output); - -FILE *srunner_open_lfile (SRunner * sr); -FILE *srunner_open_xmlfile (SRunner * sr); -FILE *srunner_open_tapfile (SRunner * sr); -void srunner_init_logging (SRunner * sr, enum print_output print_mode); -void srunner_end_logging (SRunner * sr); - -#endif /* CHECK_LOG_H */ diff --git a/libs/gst/check/libcheck/check_msg.c b/libs/gst/check/libcheck/check_msg.c deleted file mode 100644 index e862c5c706..0000000000 --- a/libs/gst/check/libcheck/check_msg.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat/libcompat.h" - -#include <sys/types.h> -#include <stdlib.h> -#include <fcntl.h> -#include <stdio.h> - -#include "check_error.h" -#include "internal-check.h" -#include "check_list.h" -#include "check_impl.h" -#include "check_msg.h" -#include "check_pack.h" -#include "check_str.h" - - -/* 'Pipe' is implemented as a temporary file to overcome message - * volume limitations outlined in bug #482012. This scheme works well - * with the existing usage wherein the parent does not begin reading - * until the child has done writing and exited. - * - * Pipe life cycle: - * - The parent creates a tmpfile(). - * - The fork() call has the effect of duplicating the file descriptor - * and copying (on write) the FILE* data structures. - * - The child writes to the file, and its dup'ed file descriptor and - * data structures are cleaned up on child process exit. - * - Before reading, the parent rewind()'s the file to reset both - * FILE* and underlying file descriptor location data. - * - When finished, the parent fclose()'s the FILE*, deleting the - * temporary file, per tmpfile()'s semantics. - * - * This scheme may break down if the usage changes to asynchronous - * reading and writing. - */ - -static FILE *send_file1; -static char *send_file1_name; -static FILE *send_file2; -static char *send_file2_name; - -static FILE *get_pipe (void); -static void setup_pipe (void); -static void teardown_pipe (void); -static TestResult *construct_test_result (RcvMsg * rmsg, int waserror); -static void tr_set_loc_by_ctx (TestResult * tr, enum ck_result_ctx ctx, - RcvMsg * rmsg); -static FILE * -get_pipe (void) -{ - if (send_file2 != 0) { - return send_file2; - } - - if (send_file1 != 0) { - return send_file1; - } - - eprintf ("No messaging setup", __FILE__, __LINE__); - - return NULL; -} - -void -send_failure_info (const char *msg) -{ - FailMsg fmsg; - - fmsg.msg = strdup (msg); - ppack (get_pipe (), CK_MSG_FAIL, (CheckMsg *) & fmsg); - free (fmsg.msg); -} - -void -send_duration_info (int duration) -{ - DurationMsg dmsg; - - dmsg.duration = duration; - ppack (get_pipe (), CK_MSG_DURATION, (CheckMsg *) & dmsg); -} - -void -send_loc_info (const char *file, int line) -{ - LocMsg lmsg; - - lmsg.file = strdup (file); - lmsg.line = line; - ppack (get_pipe (), CK_MSG_LOC, (CheckMsg *) & lmsg); - free (lmsg.file); -} - -void -send_ctx_info (enum ck_result_ctx ctx) -{ - CtxMsg cmsg; - - cmsg.ctx = ctx; - ppack (get_pipe (), CK_MSG_CTX, (CheckMsg *) & cmsg); -} - -TestResult * -receive_test_result (int waserror) -{ - FILE *fp; - RcvMsg *rmsg; - TestResult *result; - - fp = get_pipe (); - if (fp == NULL) { - eprintf ("Error in call to get_pipe", __FILE__, __LINE__ - 2); - } - - rewind (fp); - rmsg = punpack (fp); - - if (rmsg == NULL) { - eprintf ("Error in call to punpack", __FILE__, __LINE__ - 4); - } - - teardown_pipe (); - setup_pipe (); - - result = construct_test_result (rmsg, waserror); - rcvmsg_free (rmsg); - return result; -} - -static void -tr_set_loc_by_ctx (TestResult * tr, enum ck_result_ctx ctx, RcvMsg * rmsg) -{ - if (ctx == CK_CTX_TEST) { - tr->file = rmsg->test_file; - tr->line = rmsg->test_line; - rmsg->test_file = NULL; - rmsg->test_line = -1; - } else { - tr->file = rmsg->fixture_file; - tr->line = rmsg->fixture_line; - rmsg->fixture_file = NULL; - rmsg->fixture_line = -1; - } -} - -static TestResult * -construct_test_result (RcvMsg * rmsg, int waserror) -{ - TestResult *tr; - - if (rmsg == NULL) - return NULL; - - tr = tr_create (); - - if (rmsg->msg != NULL || waserror) { - if (rmsg->failctx != CK_CTX_INVALID) { - tr->ctx = rmsg->failctx; - } else { - tr->ctx = rmsg->lastctx; - } - - tr->msg = rmsg->msg; - rmsg->msg = NULL; - tr_set_loc_by_ctx (tr, tr->ctx, rmsg); - } else if (rmsg->lastctx == CK_CTX_SETUP) { - tr->ctx = CK_CTX_SETUP; - tr->msg = NULL; - tr_set_loc_by_ctx (tr, CK_CTX_SETUP, rmsg); - } else { - tr->ctx = CK_CTX_TEST; - tr->msg = NULL; - tr->duration = rmsg->duration; - tr_set_loc_by_ctx (tr, CK_CTX_TEST, rmsg); - } - - return tr; -} - -void -setup_messaging (void) -{ - setup_pipe (); -} - -void -teardown_messaging (void) -{ - teardown_pipe (); -} - -/** - * Open a temporary file. - * - * If the file could be unlinked upon creation, the name - * of the file is not returned via 'name'. However, if the - * file could not be unlinked, the name is returned, - * expecting the caller to both delete the file and - * free the 'name' field after the file is closed. - */ -FILE * -open_tmp_file (char **name) -{ - FILE *file = NULL; - - *name = NULL; - -#if !HAVE_MKSTEMP - /* Windows does not like tmpfile(). This is likely because tmpfile() - * call unlink() on the file before returning it, to make sure the - * file is deleted when it is closed. The unlink() call also fails - * on Windows if the file is still open. */ - /* also note that mkstemp is apparently a C90 replacement for tmpfile */ - /* perhaps all we need to do on Windows is set TMPDIR to whatever is - stored in TEMP for tmpfile to work */ - /* and finally, the "b" from "w+b" is ignored on OS X, not sure about WIN32 */ - - file = tmpfile (); - if (file == NULL) { - char *tmp = getenv ("TEMP"); - char *tmp_file = tempnam (tmp, "check_"); - - /* - * Note, tempnam is not enough to get a unique name. Between - * getting the name and opening the file, something else also - * calling tempnam() could get the same name. It has been observed - * on MinGW-w64 builds on Wine that this exact thing happens - * if multiple instances of a unit tests are running concurrently. - * To prevent two concurrent unit tests from getting the same file, - * we append the pid to the file. The pid should be unique on the - * system. - */ - char *uniq_tmp_file = ck_strdup_printf ("%s.%d", tmp_file, getpid ()); - - file = fopen (uniq_tmp_file, "w+b"); - *name = uniq_tmp_file; - free (tmp_file); - } -#else - int fd = -1; - const char *tmp_dir = getenv ("TEMP"); - if (!tmp_dir) { - tmp_dir = "."; - } - - *name = ck_strdup_printf ("%s/check_XXXXXX", tmp_dir); - - if (-1 < (fd = mkstemp (*name))) { - file = fdopen (fd, "w+b"); - if (0 == unlink (*name) || NULL == file) { - free (*name); - *name = NULL; - } - } -#endif - return file; -} - -static void -setup_pipe (void) -{ - if (send_file1 == NULL) { - send_file1 = open_tmp_file (&send_file1_name); - return; - } - if (send_file2 == NULL) { - send_file2 = open_tmp_file (&send_file2_name); - return; - } - eprintf ("Only one nesting of suite runs supported", __FILE__, __LINE__); -} - -static void -teardown_pipe (void) -{ - if (send_file2 != 0) { - fclose (send_file2); - send_file2 = 0; - if (send_file2_name != NULL) { - unlink (send_file2_name); - free (send_file2_name); - send_file2_name = NULL; - } - } else if (send_file1 != 0) { - fclose (send_file1); - send_file1 = 0; - if (send_file1_name != NULL) { - unlink (send_file1_name); - free (send_file1_name); - send_file1_name = NULL; - } - } else { - eprintf ("No messaging setup", __FILE__, __LINE__); - } -} diff --git a/libs/gst/check/libcheck/check_msg.h b/libs/gst/check/libcheck/check_msg.h deleted file mode 100644 index b99a757f8e..0000000000 --- a/libs/gst/check/libcheck/check_msg.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#ifndef CHECK_MSG_NEW_H -#define CHECK_MSG_NEW_H - - -/* Functions implementing messaging during test runs */ - -void send_failure_info (const char *msg); -void send_loc_info (const char *file, int line); -void send_ctx_info (enum ck_result_ctx ctx); -void send_duration_info (int duration); - -TestResult *receive_test_result (int waserror); - -void setup_messaging (void); -void teardown_messaging (void); - -FILE *open_tmp_file (char **name); - -#endif /*CHECK_MSG_NEW_H */ diff --git a/libs/gst/check/libcheck/check_pack.c b/libs/gst/check/libcheck/check_pack.c deleted file mode 100644 index 4925d88667..0000000000 --- a/libs/gst/check/libcheck/check_pack.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat/libcompat.h" - -#include <stdlib.h> -#include <string.h> -#include <stdio.h> - -#include "internal-check.h" -#include "check_error.h" -#include "check_list.h" -#include "check_impl.h" -#include "check_pack.h" - -#ifndef HAVE_PTHREAD -#define pthread_mutex_lock(arg) -#define pthread_mutex_unlock(arg) -#define pthread_cleanup_push(f,a) { -#define pthread_cleanup_pop(e) } -#endif - -/* Maximum size for one message in the message stream. */ -#define CK_MAX_MSG_SIZE 8192 -/* This is used to implement a sliding window on the receiving - * side. When sending messages, we assure that no single message - * is bigger than this (actually we check against CK_MAX_MSG_SIZE/2). - * The usual size for a message is less than 80 bytes. - * All this is done instead of the previous approach to allocate (actually - * continuously reallocate) one big chunk for the whole message stream. - * Problems were seen in the wild with up to 4 GB reallocations. - */ - - -/* typedef an unsigned int that has at least 4 bytes */ -typedef uint32_t ck_uint32; - - -static void pack_int (char **buf, int val); -static int upack_int (char **buf); -static void pack_str (char **buf, const char *str); -static char *upack_str (char **buf); - -static int pack_ctx (char **buf, CtxMsg * cmsg); -static int pack_loc (char **buf, LocMsg * lmsg); -static int pack_fail (char **buf, FailMsg * fmsg); -static int pack_duration (char **buf, DurationMsg * fmsg); -static void upack_ctx (char **buf, CtxMsg * cmsg); -static void upack_loc (char **buf, LocMsg * lmsg); -static void upack_fail (char **buf, FailMsg * fmsg); -static void upack_duration (char **buf, DurationMsg * fmsg); - -static void check_type (int type, const char *file, int line); -static enum ck_msg_type upack_type (char **buf); -static void pack_type (char **buf, enum ck_msg_type type); - -static int read_buf (FILE * fdes, int size, char *buf); -static int get_result (char *buf, RcvMsg * rmsg); -static void rcvmsg_update_ctx (RcvMsg * rmsg, enum ck_result_ctx ctx); -static void rcvmsg_update_loc (RcvMsg * rmsg, const char *file, int line); -static RcvMsg *rcvmsg_create (void); -void rcvmsg_free (RcvMsg * rmsg); - -typedef int (*pfun) (char **, CheckMsg *); -typedef void (*upfun) (char **, CheckMsg *); - -static pfun pftab[] = { - (pfun) pack_ctx, - (pfun) pack_fail, - (pfun) pack_loc, - (pfun) pack_duration -}; - -static upfun upftab[] = { - (upfun) upack_ctx, - (upfun) upack_fail, - (upfun) upack_loc, - (upfun) upack_duration -}; - -int -pack (enum ck_msg_type type, char **buf, CheckMsg * msg) -{ - if (buf == NULL) - return -1; - if (msg == NULL) - return 0; - - check_type (type, __FILE__, __LINE__); - - return pftab[type] (buf, msg); -} - -int -upack (char *buf, CheckMsg * msg, enum ck_msg_type *type) -{ - char *obuf; - - if (buf == NULL) - return -1; - - obuf = buf; - - *type = upack_type (&buf); - - check_type (*type, __FILE__, __LINE__); - - upftab[*type] (&buf, msg); - - return buf - obuf; -} - -static void -pack_int (char **buf, int val) -{ - unsigned char *ubuf = (unsigned char *) *buf; - ck_uint32 uval = val; - - ubuf[0] = (unsigned char) ((uval >> 24) & 0xFF); - ubuf[1] = (unsigned char) ((uval >> 16) & 0xFF); - ubuf[2] = (unsigned char) ((uval >> 8) & 0xFF); - ubuf[3] = (unsigned char) (uval & 0xFF); - - *buf += 4; -} - -static int -upack_int (char **buf) -{ - unsigned char *ubuf = (unsigned char *) *buf; - ck_uint32 uval; - - uval = - (ck_uint32) ((ubuf[0] << 24) | (ubuf[1] << 16) | (ubuf[2] << 8) | - ubuf[3]); - - *buf += 4; - - return (int) uval; -} - -static void -pack_str (char **buf, const char *val) -{ - int strsz; - - if (val == NULL) - strsz = 0; - else - strsz = strlen (val); - - pack_int (buf, strsz); - - if (strsz > 0) { - memcpy (*buf, val, strsz); - *buf += strsz; - } -} - -static char * -upack_str (char **buf) -{ - char *val; - int strsz; - - strsz = upack_int (buf); - - if (strsz > 0) { - val = (char *) emalloc (strsz + 1); - memcpy (val, *buf, strsz); - val[strsz] = 0; - *buf += strsz; - } else { - val = (char *) emalloc (1); - *val = 0; - } - - return val; -} - -static void -pack_type (char **buf, enum ck_msg_type type) -{ - pack_int (buf, (int) type); -} - -static enum ck_msg_type -upack_type (char **buf) -{ - return (enum ck_msg_type) upack_int (buf); -} - - -static int -pack_ctx (char **buf, CtxMsg * cmsg) -{ - char *ptr; - int len; - - len = 4 + 4; - *buf = ptr = (char *) emalloc (len); - - pack_type (&ptr, CK_MSG_CTX); - pack_int (&ptr, (int) cmsg->ctx); - - return len; -} - -static void -upack_ctx (char **buf, CtxMsg * cmsg) -{ - cmsg->ctx = (enum ck_result_ctx) upack_int (buf); -} - -static int -pack_duration (char **buf, DurationMsg * cmsg) -{ - char *ptr; - int len; - - len = 4 + 4; - *buf = ptr = (char *) emalloc (len); - - pack_type (&ptr, CK_MSG_DURATION); - pack_int (&ptr, cmsg->duration); - - return len; -} - -static void -upack_duration (char **buf, DurationMsg * cmsg) -{ - cmsg->duration = upack_int (buf); -} - -static int -pack_loc (char **buf, LocMsg * lmsg) -{ - char *ptr; - int len; - - len = 4 + 4 + (lmsg->file ? strlen (lmsg->file) : 0) + 4; - *buf = ptr = (char *) emalloc (len); - - pack_type (&ptr, CK_MSG_LOC); - pack_str (&ptr, lmsg->file); - pack_int (&ptr, lmsg->line); - - return len; -} - -static void -upack_loc (char **buf, LocMsg * lmsg) -{ - lmsg->file = upack_str (buf); - lmsg->line = upack_int (buf); -} - -static int -pack_fail (char **buf, FailMsg * fmsg) -{ - char *ptr; - int len; - - len = 4 + 4 + (fmsg->msg ? strlen (fmsg->msg) : 0); - *buf = ptr = (char *) emalloc (len); - - pack_type (&ptr, CK_MSG_FAIL); - pack_str (&ptr, fmsg->msg); - - return len; -} - -static void -upack_fail (char **buf, FailMsg * fmsg) -{ - fmsg->msg = upack_str (buf); -} - -static void -check_type (int type, const char *file, int line) -{ - if (type < 0 || type >= CK_MSG_LAST) - eprintf ("Bad message type arg %d", file, line, type); -} - -#ifdef HAVE_PTHREAD -static pthread_mutex_t ck_mutex_lock = PTHREAD_MUTEX_INITIALIZER; -static void -ppack_cleanup (void *mutex) -{ - pthread_mutex_unlock ((pthread_mutex_t *) mutex); -} -#endif - -void -ppack (FILE * fdes, enum ck_msg_type type, CheckMsg * msg) -{ - char *buf = NULL; - int n; - ssize_t r; - - n = pack (type, &buf, msg); - /* Keep it on the safe side to not send too much data. */ - if (n > (CK_MAX_MSG_SIZE / 2)) - eprintf ("Message string too long", __FILE__, __LINE__ - 2); - - pthread_cleanup_push (ppack_cleanup, &ck_mutex_lock); - pthread_mutex_lock (&ck_mutex_lock); - r = fwrite (buf, 1, n, fdes); - fflush (fdes); - pthread_mutex_unlock (&ck_mutex_lock); - pthread_cleanup_pop (0); - if (r != n) - eprintf ("Error in call to fwrite:", __FILE__, __LINE__ - 2); - - free (buf); -} - -static int -read_buf (FILE * fdes, int size, char *buf) -{ - int n; - - n = fread (buf, 1, size, fdes); - - if (ferror (fdes)) { - eprintf ("Error in call to fread:", __FILE__, __LINE__ - 4); - } - - return n; -} - -static int -get_result (char *buf, RcvMsg * rmsg) -{ - enum ck_msg_type type; - CheckMsg msg; - int n; - - n = upack (buf, &msg, &type); - if (n == -1) - eprintf ("Error in call to upack", __FILE__, __LINE__ - 2); - - if (type == CK_MSG_CTX) { - CtxMsg *cmsg = (CtxMsg *) & msg; - - rcvmsg_update_ctx (rmsg, cmsg->ctx); - } else if (type == CK_MSG_LOC) { - LocMsg *lmsg = (LocMsg *) & msg; - - if (rmsg->failctx == CK_CTX_INVALID) { - rcvmsg_update_loc (rmsg, lmsg->file, lmsg->line); - } - free (lmsg->file); - } else if (type == CK_MSG_FAIL) { - FailMsg *fmsg = (FailMsg *) & msg; - - if (rmsg->msg == NULL) { - rmsg->msg = strdup (fmsg->msg); - rmsg->failctx = rmsg->lastctx; - } else { - /* Skip subsequent failure messages, only happens for CK_NOFORK */ - } - free (fmsg->msg); - } else if (type == CK_MSG_DURATION) { - DurationMsg *cmsg = (DurationMsg *) & msg; - - rmsg->duration = cmsg->duration; - } else - check_type (type, __FILE__, __LINE__); - - return n; -} - -static void -reset_rcv_test (RcvMsg * rmsg) -{ - rmsg->test_line = -1; - rmsg->test_file = NULL; -} - -static void -reset_rcv_fixture (RcvMsg * rmsg) -{ - rmsg->fixture_line = -1; - rmsg->fixture_file = NULL; -} - -static RcvMsg * -rcvmsg_create (void) -{ - RcvMsg *rmsg; - - rmsg = (RcvMsg *) emalloc (sizeof (RcvMsg)); - rmsg->lastctx = CK_CTX_INVALID; - rmsg->failctx = CK_CTX_INVALID; - rmsg->msg = NULL; - rmsg->duration = -1; - reset_rcv_test (rmsg); - reset_rcv_fixture (rmsg); - return rmsg; -} - -void -rcvmsg_free (RcvMsg * rmsg) -{ - free (rmsg->fixture_file); - free (rmsg->test_file); - free (rmsg->msg); - free (rmsg); -} - -static void -rcvmsg_update_ctx (RcvMsg * rmsg, enum ck_result_ctx ctx) -{ - if (rmsg->lastctx != CK_CTX_INVALID) { - free (rmsg->fixture_file); - reset_rcv_fixture (rmsg); - } - rmsg->lastctx = ctx; -} - -static void -rcvmsg_update_loc (RcvMsg * rmsg, const char *file, int line) -{ - if (rmsg->lastctx == CK_CTX_TEST) { - free (rmsg->test_file); - rmsg->test_line = line; - rmsg->test_file = strdup (file); - } else { - free (rmsg->fixture_file); - rmsg->fixture_line = line; - rmsg->fixture_file = strdup (file); - } -} - -RcvMsg * -punpack (FILE * fdes) -{ - int nread, nparse, n; - char *buf; - RcvMsg *rmsg; - - rmsg = rcvmsg_create (); - - /* Allocate a buffer */ - buf = (char *) emalloc (CK_MAX_MSG_SIZE); - /* Fill the buffer from the file */ - nread = read_buf (fdes, CK_MAX_MSG_SIZE, buf); - nparse = nread; - /* While not all parsed */ - while (nparse > 0) { - /* Parse one message */ - n = get_result (buf, rmsg); - nparse -= n; - if (nparse < 0) - eprintf ("Error in call to get_result", __FILE__, __LINE__ - 3); - /* Move remaining data in buffer to the beginning */ - memmove (buf, buf + n, nparse); - /* If EOF has not been seen */ - if (nread > 0) { - /* Read more data into empty space at end of the buffer */ - nread = read_buf (fdes, n, buf + nparse); - nparse += nread; - } - } - free (buf); - - if (rmsg->lastctx == CK_CTX_INVALID) { - free (rmsg); - rmsg = NULL; - } - - return rmsg; -} diff --git a/libs/gst/check/libcheck/check_pack.h b/libs/gst/check/libcheck/check_pack.h deleted file mode 100644 index e73a196e64..0000000000 --- a/libs/gst/check/libcheck/check_pack.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#ifndef CHECK_PACK_H -#define CHECK_PACK_H - - -enum ck_msg_type -{ - CK_MSG_CTX, - CK_MSG_FAIL, - CK_MSG_LOC, - CK_MSG_DURATION, - CK_MSG_LAST -}; - -typedef struct CtxMsg -{ - enum ck_result_ctx ctx; -} CtxMsg; - -typedef struct LocMsg -{ - int line; - char *file; -} LocMsg; - -typedef struct FailMsg -{ - char *msg; -} FailMsg; - -typedef struct DurationMsg -{ - int duration; -} DurationMsg; - -typedef union -{ - CtxMsg ctx_msg; - FailMsg fail_msg; - LocMsg loc_msg; - DurationMsg duration_msg; -} CheckMsg; - -typedef struct RcvMsg -{ - enum ck_result_ctx lastctx; - enum ck_result_ctx failctx; - char *fixture_file; - int fixture_line; - char *test_file; - int test_line; - char *msg; - int duration; -} RcvMsg; - -void rcvmsg_free (RcvMsg * rmsg); - - -int pack (enum ck_msg_type type, char **buf, CheckMsg * msg); -int upack (char *buf, CheckMsg * msg, enum ck_msg_type *type); - -void ppack (FILE * fdes, enum ck_msg_type type, CheckMsg * msg); -RcvMsg *punpack (FILE * fdes); - -#endif /*CHECK_PACK_H */ diff --git a/libs/gst/check/libcheck/check_print.c b/libs/gst/check/libcheck/check_print.c deleted file mode 100644 index c94e47c80e..0000000000 --- a/libs/gst/check/libcheck/check_print.c +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat/libcompat.h" - -#include <stdio.h> -#include <string.h> -#include <stdlib.h> - -#include "internal-check.h" -#include "check_list.h" -#include "check_impl.h" -#include "check_str.h" -#include "check_print.h" - -static void srunner_fprint_summary (FILE * file, SRunner * sr, - enum print_output print_mode); -static void srunner_fprint_results (FILE * file, SRunner * sr, - enum print_output print_mode); - - -void -srunner_print (SRunner * sr, enum print_output print_mode) -{ - srunner_fprint (stdout, sr, print_mode); -} - -void -srunner_fprint (FILE * file, SRunner * sr, enum print_output print_mode) -{ - if (print_mode == CK_ENV) { - print_mode = get_env_printmode (); - } - - srunner_fprint_summary (file, sr, print_mode); - srunner_fprint_results (file, sr, print_mode); -} - -static void -srunner_fprint_summary (FILE * file, SRunner * sr, enum print_output print_mode) -{ -#if ENABLE_SUBUNIT - if (print_mode == CK_SUBUNIT) - return; -#endif - - if (print_mode >= CK_MINIMAL) { - char *str; - - str = sr_stat_str (sr); - fprintf (file, "%s\n", str); - free (str); - } - return; -} - -static void -srunner_fprint_results (FILE * file, SRunner * sr, enum print_output print_mode) -{ - List *resultlst; - -#if ENABLE_SUBUNIT - if (print_mode == CK_SUBUNIT) - return; -#endif - - resultlst = sr->resultlst; - - for (check_list_front (resultlst); !check_list_at_end (resultlst); - check_list_advance (resultlst)) { - TestResult *tr = (TestResult *) check_list_val (resultlst); - - tr_fprint (file, tr, print_mode); - } - return; -} - -void -fprint_xml_esc (FILE * file, const char *str) -{ - /* The valid XML characters are as follows: - * #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] - * Characters that are outside of ASCII must be encoded. Further, the - * following special characters: - * " ' < > & - * must be encoded. We assume that the incoming string may be a multibyte - * character. - */ - - for (; *str != '\0'; str++) { - char next = *str; - - /* handle special characters that must be escaped */ - if (next == '"' || next == '\'' || next == '<' || next == '>' - || next == '&') { - switch (next) { - case '"': - fputs (""", file); - break; - case '\'': - fputs ("'", file); - break; - case '<': - fputs ("<", file); - break; - case '>': - fputs (">", file); - break; - case '&': - fputs ("&", file); - break; - } - } - /* printable ASCII */ - else if (next >= ' ' && next <= '~') { - fputc (next, file); - } - /* Non-printable character */ - else if (next == 0x9 || next == 0xA || next == 0xD || next >= 0x20) { - fprintf (file, "&#x%X;", next); - } - /* If it did not get printed, it is not a valid XML character */ - } -} - -void -tr_fprint (FILE * file, TestResult * tr, enum print_output print_mode) -{ - if (print_mode == CK_ENV) { - print_mode = get_env_printmode (); - } - - if ((print_mode >= CK_VERBOSE && tr->rtype == CK_PASS) || - (tr->rtype != CK_PASS && print_mode >= CK_NORMAL)) { - char *trstr = tr_str (tr); - - fprintf (file, "%s\n", trstr); - free (trstr); - } -} - -void -tr_xmlprint (FILE * file, TestResult * tr, - enum print_output print_mode CK_ATTRIBUTE_UNUSED) -{ - char result[10]; - char *path_name = NULL; - char *file_name = NULL; - char *slash = NULL; - - switch (tr->rtype) { - case CK_PASS: - snprintf (result, sizeof (result), "%s", "success"); - break; - case CK_FAILURE: - snprintf (result, sizeof (result), "%s", "failure"); - break; - case CK_ERROR: - snprintf (result, sizeof (result), "%s", "error"); - break; - case CK_TEST_RESULT_INVALID: - default: - abort (); - break; - } - - if (tr->file) { - slash = strrchr (tr->file, '/'); - if (slash == NULL) { - slash = strrchr (tr->file, '\\'); - } - - if (slash == NULL) { - path_name = strdup ("."); - file_name = tr->file; - } else { - path_name = strdup (tr->file); - path_name[slash - tr->file] = 0; /* Terminate the temporary string. */ - file_name = slash + 1; - } - } - - - fprintf (file, " <test result=\"%s\">\n", result); - fprintf (file, " <path>%s</path>\n", - (path_name == NULL ? "" : path_name)); - fprintf (file, " <fn>%s:%d</fn>\n", - (file_name == NULL ? "" : file_name), tr->line); - fprintf (file, " <id>%s</id>\n", tr->tname); - fprintf (file, " <iteration>%d</iteration>\n", tr->iter); - fprintf (file, " <duration>%d.%06d</duration>\n", - tr->duration < 0 ? -1 : tr->duration / US_PER_SEC, - tr->duration < 0 ? 0 : tr->duration % US_PER_SEC); - fprintf (file, " <description>"); - fprint_xml_esc (file, tr->tcname); - fprintf (file, "</description>\n"); - fprintf (file, " <message>"); - fprint_xml_esc (file, tr->msg); - fprintf (file, "</message>\n"); - fprintf (file, " </test>\n"); - - free (path_name); -} - -enum print_output -get_env_printmode (void) -{ - char *env = getenv ("CK_VERBOSITY"); - - if (env == NULL) - return CK_NORMAL; - if (strcmp (env, "silent") == 0) - return CK_SILENT; - if (strcmp (env, "minimal") == 0) - return CK_MINIMAL; - if (strcmp (env, "verbose") == 0) - return CK_VERBOSE; - return CK_NORMAL; -} diff --git a/libs/gst/check/libcheck/check_print.h b/libs/gst/check/libcheck/check_print.h deleted file mode 100644 index 20c0d221d4..0000000000 --- a/libs/gst/check/libcheck/check_print.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#ifndef CHECK_PRINT_H -#define CHECK_PRINT_H - -/* escape XML special characters (" ' < > &) in str and print to file */ -void fprint_xml_esc (FILE * file, const char *str); -void tr_fprint (FILE * file, TestResult * tr, enum print_output print_mode); -void tr_xmlprint (FILE * file, TestResult * tr, enum print_output print_mode); -void srunner_fprint (FILE * file, SRunner * sr, enum print_output print_mode); -enum print_output get_env_printmode (void); - - -#endif /* CHECK_PRINT_H */ diff --git a/libs/gst/check/libcheck/check_run.c b/libs/gst/check/libcheck/check_run.c deleted file mode 100644 index a97379c91a..0000000000 --- a/libs/gst/check/libcheck/check_run.c +++ /dev/null @@ -1,801 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat/libcompat.h" - -#include <sys/types.h> -#include <time.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <stdarg.h> -#include <signal.h> -#include <setjmp.h> - -#include <glib.h> - -#include "internal-check.h" -#include "check_error.h" -#include "check_list.h" -#include "check_impl.h" -#include "check_msg.h" -#include "check_log.h" - -enum rinfo -{ - CK_R_SIG, - CK_R_PASS, - CK_R_EXIT, - CK_R_FAIL_TEST, - CK_R_FAIL_FIXTURE -}; - -enum tf_type -{ - CK_FORK_TEST, - CK_NOFORK_TEST, - CK_NOFORK_FIXTURE -}; - - -/* all functions are defined in the same order they are declared. - functions that depend on forking are gathered all together. - non-static functions are at the end of the file. */ -static void srunner_run_init (SRunner * sr, enum print_output print_mode); -static void srunner_run_end (SRunner * sr, enum print_output print_mode); -static void srunner_iterate_suites (SRunner * sr, - const char *sname, const char *tcname, - const char *include_tags, - const char *exclude_tags, enum print_output print_mode); -static void srunner_iterate_tcase_tfuns (SRunner * sr, TCase * tc); -static void srunner_add_failure (SRunner * sr, TestResult * tf); -static TestResult *srunner_run_setup (List * func_list, - enum fork_status fork_usage, const char *test_name, const char *setup_name); -static int srunner_run_unchecked_setup (SRunner * sr, TCase * tc); -static TestResult *tcase_run_checked_setup (SRunner * sr, TCase * tc); -static void srunner_run_teardown (List * fixture_list, - enum fork_status fork_usage); -static void srunner_run_unchecked_teardown (SRunner * sr, TCase * tc); -static void tcase_run_checked_teardown (TCase * tc); -static void srunner_run_tcase (SRunner * sr, TCase * tc); -static TestResult *tcase_run_tfun_nofork (SRunner * sr, TCase * tc, TF * tf, - int i); -static TestResult *receive_result_info_nofork (const char *tcname, - const char *tname, int iter, int duration); -static void set_nofork_info (TestResult * tr); -static char *pass_msg (void); - -#if defined(HAVE_FORK) && HAVE_FORK==1 -static TestResult *tcase_run_tfun_fork (SRunner * sr, TCase * tc, TF * tf, - int i); -static TestResult *receive_result_info_fork (const char *tcname, - const char *tname, int iter, - int status, int expected_signal, signed char allowed_exit_value); -static void set_fork_info (TestResult * tr, int status, int expected_signal, - signed char allowed_exit_value); -static char *signal_msg (int sig); -static char *signal_error_msg (int signal_received, int signal_expected); -static char *exit_msg (int exitstatus); -static int waserror (int status, int expected_signal); - -static int alarm_received; -static pid_t group_pid; -static struct sigaction sigint_old_action; -static struct sigaction sigterm_old_action; - -static void CK_ATTRIBUTE_UNUSED -sig_handler (int sig_nr) -{ - switch (sig_nr) { - case SIGALRM: - alarm_received = 1; - killpg (group_pid, SIGKILL); - break; - case SIGTERM: - case SIGINT: - { - pid_t own_group_pid; - int child_sig = SIGTERM; - - if (sig_nr == SIGINT) { - child_sig = SIGKILL; - sigaction (SIGINT, &sigint_old_action, NULL); - } else { - sigaction (SIGTERM, &sigterm_old_action, NULL); - } - - killpg (group_pid, child_sig); - - /* POSIX says that calling killpg(0) - * does not necessarily mean to call it on the callers - * group pid! */ - own_group_pid = getpgrp (); - killpg (own_group_pid, sig_nr); - break; - } - default: - eprintf ("Unhandled signal: %d", __FILE__, __LINE__, sig_nr); - break; - } -} -#endif /* HAVE_FORK */ - -#define MSG_LEN 100 - -static void -srunner_run_init (SRunner * sr, enum print_output print_mode) -{ - set_fork_status (srunner_fork_status (sr)); - setup_messaging (); - srunner_init_logging (sr, print_mode); - log_srunner_start (sr); -} - -static void -srunner_run_end (SRunner * sr, enum print_output CK_ATTRIBUTE_UNUSED print_mode) -{ - log_srunner_end (sr); - srunner_end_logging (sr); - teardown_messaging (); - set_fork_status (CK_FORK); -} - -static void -srunner_iterate_suites (SRunner * sr, - const char *sname, const char *tcname, - const char *include_tags, - const char *exclude_tags, enum print_output CK_ATTRIBUTE_UNUSED print_mode) -{ - List *include_tag_lst; - List *exclude_tag_lst; - List *slst; - List *tcl; - TCase *tc; - - slst = sr->slst; - - include_tag_lst = tag_string_to_list (include_tags); - exclude_tag_lst = tag_string_to_list (exclude_tags); - - for (check_list_front (slst); !check_list_at_end (slst); - check_list_advance (slst)) { - Suite *s = (Suite *) check_list_val (slst); - - if (((sname != NULL) && (strcmp (sname, s->name) != 0)) - || ((tcname != NULL) && (!suite_tcase (s, tcname)))) - continue; - - log_suite_start (sr, s); - - tcl = s->tclst; - - for (check_list_front (tcl); !check_list_at_end (tcl); - check_list_advance (tcl)) { - tc = (TCase *) check_list_val (tcl); - - if ((tcname != NULL) && (strcmp (tcname, tc->name) != 0)) { - continue; - } - if (include_tags != NULL) { - if (!tcase_matching_tag (tc, include_tag_lst)) { - continue; - } - } - if (exclude_tags != NULL) { - if (tcase_matching_tag (tc, exclude_tag_lst)) { - continue; - } - } - - srunner_run_tcase (sr, tc); - } - - log_suite_end (sr, s); - } - - check_list_apply (include_tag_lst, free); - check_list_apply (exclude_tag_lst, free); - check_list_free (include_tag_lst); - check_list_free (exclude_tag_lst); -} - -static void -srunner_iterate_tcase_tfuns (SRunner * sr, TCase * tc) -{ - List *tfl; - TF *tfun; - TestResult *tr = NULL; - - tfl = tc->tflst; - - for (check_list_front (tfl); !check_list_at_end (tfl); - check_list_advance (tfl)) { - int i; - - tfun = (TF *) check_list_val (tfl); - - for (i = tfun->loop_start; i < tfun->loop_end; i++) { - log_test_start (sr, tc, tfun); - switch (srunner_fork_status (sr)) { - case CK_FORK: -#if defined(HAVE_FORK) && HAVE_FORK==1 - tr = tcase_run_tfun_fork (sr, tc, tfun, i); -#else /* HAVE_FORK */ - eprintf ("This version does not support fork", __FILE__, __LINE__); -#endif /* HAVE_FORK */ - break; - case CK_NOFORK: - tr = tcase_run_tfun_nofork (sr, tc, tfun, i); - break; - case CK_FORK_GETENV: - default: - eprintf ("Bad fork status in SRunner", __FILE__, __LINE__); - } - - if (NULL != tr) { - srunner_add_failure (sr, tr); - log_test_end (sr, tr); - } - } - } -} - -static void -srunner_add_failure (SRunner * sr, TestResult * tr) -{ - check_list_add_end (sr->resultlst, tr); - sr->stats->n_checked++; /* count checks during setup, test, and teardown */ - if (tr->rtype == CK_FAILURE) - sr->stats->n_failed++; - else if (tr->rtype == CK_ERROR) - sr->stats->n_errors++; - -} - -static TestResult * -srunner_run_setup (List * fixture_list, enum fork_status fork_usage, - const char *test_name, const char *setup_name) -{ - TestResult *tr = NULL; - Fixture *setup_fixture; - - if (fork_usage == CK_FORK) { - send_ctx_info (CK_CTX_SETUP); - } - - for (check_list_front (fixture_list); !check_list_at_end (fixture_list); - check_list_advance (fixture_list)) { - setup_fixture = (Fixture *) check_list_val (fixture_list); - - if (fork_usage == CK_NOFORK) { - send_ctx_info (CK_CTX_SETUP); - - if (0 == setjmp (error_jmp_buffer)) { - setup_fixture->fun (); - } - - /* Stop the setup and return the failure in nofork mode. */ - tr = receive_result_info_nofork (test_name, setup_name, 0, -1); - if (tr->rtype != CK_PASS) { - break; - } - - free (tr->file); - free (tr->msg); - free (tr); - tr = NULL; - } else { - setup_fixture->fun (); - } - } - - return tr; -} - -static int -srunner_run_unchecked_setup (SRunner * sr, TCase * tc) -{ - TestResult *tr = NULL; - int rval = 1; - - set_fork_status (CK_NOFORK); - tr = srunner_run_setup (tc->unch_sflst, CK_NOFORK, tc->name, - "unchecked_setup"); - set_fork_status (srunner_fork_status (sr)); - - if (tr != NULL && tr->rtype != CK_PASS) { - srunner_add_failure (sr, tr); - rval = 0; - } - - return rval; -} - -static TestResult * -tcase_run_checked_setup (SRunner * sr, TCase * tc) -{ - TestResult *tr = srunner_run_setup (tc->ch_sflst, srunner_fork_status (sr), - tc->name, "checked_setup"); - - return tr; -} - -static void -srunner_run_teardown (List * fixture_list, enum fork_status fork_usage) -{ - Fixture *fixture; - - for (check_list_front (fixture_list); !check_list_at_end (fixture_list); - check_list_advance (fixture_list)) { - fixture = (Fixture *) check_list_val (fixture_list); - send_ctx_info (CK_CTX_TEARDOWN); - - if (fork_usage == CK_NOFORK) { - if (0 == setjmp (error_jmp_buffer)) { - fixture->fun (); - } else { - /* Abort the remaining teardowns */ - break; - } - } else { - fixture->fun (); - } - } -} - -static void -srunner_run_unchecked_teardown (SRunner * sr, TCase * tc) -{ - srunner_run_teardown (tc->unch_tflst, srunner_fork_status (sr)); -} - -static void -tcase_run_checked_teardown (TCase * tc) -{ - srunner_run_teardown (tc->ch_tflst, CK_NOFORK); -} - -static void -srunner_run_tcase (SRunner * sr, TCase * tc) -{ - if (srunner_run_unchecked_setup (sr, tc)) { - srunner_iterate_tcase_tfuns (sr, tc); - srunner_run_unchecked_teardown (sr, tc); - } -} - -static TestResult * -tcase_run_tfun_nofork (SRunner * sr, TCase * tc, TF * tfun, int i) -{ - TestResult *tr; - struct timespec ts_start = { 0, 0 }, ts_end = { - 0, 0}; - - tr = tcase_run_checked_setup (sr, tc); - if (tr == NULL) { - clock_gettime (check_get_clockid (), &ts_start); - if (0 == setjmp (error_jmp_buffer)) { - tfun->fn (i); - } - clock_gettime (check_get_clockid (), &ts_end); - tcase_run_checked_teardown (tc); - return receive_result_info_nofork (tc->name, tfun->name, i, - DIFF_IN_USEC (ts_start, ts_end)); - } - - return tr; -} - -static TestResult * -receive_result_info_nofork (const char *tcname, - const char *tname, int iter, int duration) -{ - TestResult *tr; - - tr = receive_test_result (0); - if (tr == NULL) { - eprintf ("Failed to receive test result", __FILE__, __LINE__); - } else { - tr->tcname = tcname; - tr->tname = tname; - tr->iter = iter; - tr->duration = duration; - set_nofork_info (tr); - } - - return tr; -} - -static void -set_nofork_info (TestResult * tr) -{ - if (tr->msg == NULL) { - tr->rtype = CK_PASS; - tr->msg = pass_msg (); - } else { - tr->rtype = CK_FAILURE; - } -} - -static char * -pass_msg (void) -{ - return strdup ("Passed"); -} - -#if defined(HAVE_FORK) && HAVE_FORK==1 -static TestResult * -tcase_run_tfun_fork (SRunner * sr, TCase * tc, TF * tfun, int i) -{ - pid_t pid_w; - pid_t pid; - int status = 0; - struct timespec ts_start = { 0, 0 }, ts_end = { - 0, 0}; - - timer_t timerid; - struct itimerspec timer_spec; - TestResult *tr; - - - pid = fork (); - if (pid == -1) - eprintf ("Error in call to fork:", __FILE__, __LINE__ - 2); - if (pid == 0) { - setpgid (0, 0); - group_pid = getpgrp (); - tr = tcase_run_checked_setup (sr, tc); - free (tr); - clock_gettime (check_get_clockid (), &ts_start); - tfun->fn (i); - clock_gettime (check_get_clockid (), &ts_end); - tcase_run_checked_teardown (tc); - send_duration_info (DIFF_IN_USEC (ts_start, ts_end)); - g_thread_pool_stop_unused_threads (); - exit (EXIT_SUCCESS); - } else { - group_pid = pid; - } - - alarm_received = 0; - - if (timer_create (check_get_clockid (), - NULL /* fire SIGALRM if timer expires */ , - &timerid) == 0) { - /* Set the timer to fire once */ - timer_spec.it_value = tc->timeout; - timer_spec.it_interval.tv_sec = 0; - timer_spec.it_interval.tv_nsec = 0; - if (timer_settime (timerid, 0, &timer_spec, NULL) == 0) { - do { - pid_w = waitpid (pid, &status, 0); - } - while (pid_w == -1); - } else { - eprintf ("Error in call to timer_settime:", __FILE__, __LINE__); - } - - /* If the timer has not fired, disable it */ - timer_delete (timerid); - } else { - eprintf ("Error in call to timer_create:", __FILE__, __LINE__); - } - - killpg (pid, SIGKILL); /* Kill remaining processes. */ - - return receive_result_info_fork (tc->name, tfun->name, i, status, - tfun->signal, tfun->allowed_exit_value); -} - -static TestResult * -receive_result_info_fork (const char *tcname, - const char *tname, - int iter, int status, int expected_signal, signed char allowed_exit_value) -{ - TestResult *tr; - - tr = receive_test_result (waserror (status, expected_signal)); - if (tr == NULL) { - eprintf ("Failed to receive test result", __FILE__, __LINE__); - } else { - tr->tcname = tcname; - tr->tname = tname; - tr->iter = iter; - set_fork_info (tr, status, expected_signal, allowed_exit_value); - } - - return tr; -} - -static void -set_fork_info (TestResult * tr, int status, int signal_expected, - signed char allowed_exit_value) -{ - int was_sig = WIFSIGNALED (status); - int was_exit = WIFEXITED (status); - signed char exit_status = WEXITSTATUS (status); - int signal_received = WTERMSIG (status); - - if (was_sig) { - if (signal_expected == signal_received) { - if (alarm_received) { - /* Got alarm instead of signal */ - tr->rtype = CK_ERROR; - if (tr->msg != NULL) { - free (tr->msg); - } - tr->msg = signal_error_msg (signal_received, signal_expected); - } else { - tr->rtype = CK_PASS; - if (tr->msg != NULL) { - free (tr->msg); - } - tr->msg = pass_msg (); - } - } else if (signal_expected != 0) { - /* signal received, but not the expected one */ - tr->rtype = CK_ERROR; - if (tr->msg != NULL) { - free (tr->msg); - } - tr->msg = signal_error_msg (signal_received, signal_expected); - } else { - /* signal received and none expected */ - tr->rtype = CK_ERROR; - if (tr->msg != NULL) { - free (tr->msg); - } - tr->msg = signal_msg (signal_received); - } - } else if (signal_expected == 0) { - if (was_exit && exit_status == allowed_exit_value) { - tr->rtype = CK_PASS; - if (tr->msg != NULL) { - free (tr->msg); - } - tr->msg = pass_msg (); - } else if (was_exit && exit_status != allowed_exit_value) { - if (tr->msg == NULL) { /* early exit */ - tr->rtype = CK_ERROR; - tr->msg = exit_msg (exit_status); - } else { - tr->rtype = CK_FAILURE; - } - } - } else { /* a signal was expected and none raised */ - if (was_exit) { - if (tr->msg != NULL) { - free (tr->msg); - } - tr->msg = exit_msg (exit_status); - tr->rtype = CK_FAILURE; /* normal exit status */ - } - } -} - -static char * -signal_msg (int signal) -{ - char *msg = (char *) emalloc (MSG_LEN); /* free'd by caller */ - - if (alarm_received) { - snprintf (msg, MSG_LEN, "Test timeout expired"); - } else { - snprintf (msg, MSG_LEN, "Received signal %d (%s)", - signal, strsignal (signal)); - } - return msg; -} - -static char * -signal_error_msg (int signal_received, int signal_expected) -{ - char *sig_r_str; - char *sig_e_str; - char *msg = (char *) emalloc (MSG_LEN); /* free'd by caller */ - - sig_r_str = strdup (strsignal (signal_received)); - sig_e_str = strdup (strsignal (signal_expected)); - if (alarm_received) { - snprintf (msg, MSG_LEN, - "Test timeout expired, expected signal %d (%s)", - signal_expected, sig_e_str); - } else { - snprintf (msg, MSG_LEN, "Received signal %d (%s), expected %d (%s)", - signal_received, sig_r_str, signal_expected, sig_e_str); - } - free (sig_r_str); - free (sig_e_str); - return msg; -} - -static char * -exit_msg (int exitval) -{ - char *msg = (char *) emalloc (MSG_LEN); /* free'd by caller */ - - snprintf (msg, MSG_LEN, "Early exit with return value %d", exitval); - return msg; -} - -static int -waserror (int status, int signal_expected) -{ - int was_sig = WIFSIGNALED (status); - int was_exit = WIFEXITED (status); - int exit_status = WEXITSTATUS (status); - int signal_received = WTERMSIG (status); - - return ((was_sig && (signal_received != signal_expected)) || - (was_exit && exit_status != 0)); -} -#endif /* HAVE_FORK */ - -enum fork_status -srunner_fork_status (SRunner * sr) -{ - if (sr->fstat == CK_FORK_GETENV) { - char *env = getenv ("CK_FORK"); - - if (env == NULL) -#if defined(HAVE_FORK) && HAVE_FORK==1 - return CK_FORK; -#else - return CK_NOFORK; -#endif - if (strcmp (env, "no") == 0) - return CK_NOFORK; - else { -#if defined(HAVE_FORK) && HAVE_FORK==1 - return CK_FORK; -#else /* HAVE_FORK */ - eprintf ("This version does not support fork", __FILE__, __LINE__); - /* Ignoring, as Check is not compiled with fork support. */ - return CK_NOFORK; -#endif /* HAVE_FORK */ - } - } else - return sr->fstat; -} - -void -srunner_set_fork_status (SRunner * sr, enum fork_status fstat) -{ -#if !defined(HAVE_FORK) || HAVE_FORK==0 - /* If fork() is unavailable, do not allow a fork mode to be set */ - if (fstat != CK_NOFORK) { - eprintf ("This version does not support fork", __FILE__, __LINE__); - /* Overriding, as Check is not compiled with fork support. */ - fstat = CK_NOFORK; - } -#endif /* ! HAVE_FORK */ - sr->fstat = fstat; -} - -void -srunner_run_all (SRunner * sr, enum print_output print_mode) -{ - srunner_run (sr, NULL, /* All test suites. */ - NULL, /* All test cases. */ - print_mode); -} - -void -srunner_run_tagged (SRunner * sr, const char *sname, const char *tcname, - const char *include_tags, const char *exclude_tags, - enum print_output print_mode) -{ -#if defined(HAVE_SIGACTION) && defined(HAVE_FORK) - static struct sigaction sigalarm_old_action; - static struct sigaction sigalarm_new_action; - static struct sigaction sigint_new_action; - static struct sigaction sigterm_new_action; -#endif /* HAVE_SIGACTION && HAVE_FORK */ - - /* Get the selected test suite and test case from the - environment. */ - if (!tcname) - tcname = getenv ("CK_RUN_CASE"); - if (!sname) - sname = getenv ("CK_RUN_SUITE"); - if (!include_tags) - include_tags = getenv ("CK_INCLUDE_TAGS"); - if (!exclude_tags) - exclude_tags = getenv ("CK_EXCLUDE_TAGS"); - - if (sr == NULL) - return; - if (print_mode >= CK_LAST) { - eprintf ("Bad print_mode argument to srunner_run_all: %d", - __FILE__, __LINE__, print_mode); - } -#if defined(HAVE_SIGACTION) && defined(HAVE_FORK) - memset (&sigalarm_new_action, 0, sizeof (sigalarm_new_action)); - sigalarm_new_action.sa_handler = sig_handler; - sigaction (SIGALRM, &sigalarm_new_action, &sigalarm_old_action); - - memset (&sigint_new_action, 0, sizeof (sigint_new_action)); - sigint_new_action.sa_handler = sig_handler; - sigaction (SIGINT, &sigint_new_action, &sigint_old_action); - - memset (&sigterm_new_action, 0, sizeof (sigterm_new_action)); - sigterm_new_action.sa_handler = sig_handler; - sigaction (SIGTERM, &sigterm_new_action, &sigterm_old_action); -#endif /* HAVE_SIGACTION && HAVE_FORK */ - srunner_run_init (sr, print_mode); - srunner_iterate_suites (sr, sname, tcname, include_tags, exclude_tags, - print_mode); - srunner_run_end (sr, print_mode); -#if defined(HAVE_SIGACTION) && defined(HAVE_FORK) - sigaction (SIGALRM, &sigalarm_old_action, NULL); - sigaction (SIGINT, &sigint_old_action, NULL); - sigaction (SIGTERM, &sigterm_old_action, NULL); -#endif /* HAVE_SIGACTION && HAVE_FORK */ -} - -void -srunner_run (SRunner * sr, const char *sname, const char *tcname, - enum print_output print_mode) -{ - srunner_run_tagged (sr, sname, tcname, NULL, NULL, print_mode); -} - -pid_t -check_fork (void) -{ -#if defined(HAVE_FORK) && HAVE_FORK==1 - pid_t pid = fork (); - - /* Set the process to a process group to be able to kill it easily. */ - if (pid >= 0) { - setpgid (pid, group_pid); - } - return pid; -#else /* HAVE_FORK */ - eprintf ("This version does not support fork", __FILE__, __LINE__); - return 0; -#endif /* HAVE_FORK */ -} - -void -check_waitpid_and_exit (pid_t pid CK_ATTRIBUTE_UNUSED) -{ -#if defined(HAVE_FORK) && HAVE_FORK==1 - pid_t pid_w; - int status; - - if (pid > 0) { - do { - pid_w = waitpid (pid, &status, 0); - } - while (pid_w == -1); - if (waserror (status, 0)) { - g_thread_pool_stop_unused_threads (); - exit (EXIT_FAILURE); - } - } - g_thread_pool_stop_unused_threads (); - exit (EXIT_SUCCESS); -#else /* HAVE_FORK */ - eprintf ("This version does not support fork", __FILE__, __LINE__); - /* Ignoring, as Check is not compiled with fork support. */ - exit (EXIT_FAILURE); -#endif /* HAVE_FORK */ -} diff --git a/libs/gst/check/libcheck/check_str.c b/libs/gst/check/libcheck/check_str.c deleted file mode 100644 index b2e64071b5..0000000000 --- a/libs/gst/check/libcheck/check_str.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat/libcompat.h" - -#include <stdio.h> -#include <stdarg.h> - -#include "internal-check.h" -#include "check_list.h" -#include "check_error.h" -#include "check_impl.h" -#include "check_str.h" - -static const char *tr_type_str (TestResult * tr); -static int percent_passed (TestStats * t); - -char * -tr_str (TestResult * tr) -{ - const char *exact_msg; - char *rstr; - - exact_msg = (tr->rtype == CK_ERROR) ? "(after this point) " : ""; - - rstr = ck_strdup_printf ("%s:%d:%s:%s:%s:%d: %s%s", - tr->file, tr->line, - tr_type_str (tr), tr->tcname, tr->tname, tr->iter, exact_msg, tr->msg); - - return rstr; -} - -char * -tr_short_str (TestResult * tr) -{ - const char *exact_msg; - char *rstr; - - exact_msg = (tr->rtype == CK_ERROR) ? "(after this point) " : ""; - - rstr = ck_strdup_printf ("%s:%d: %s%s", - tr->file, tr->line, exact_msg, tr->msg); - - return rstr; -} - -char * -sr_stat_str (SRunner * sr) -{ - char *str; - TestStats *ts; - - ts = sr->stats; - - str = ck_strdup_printf ("%d%%: Checks: %d, Failures: %d, Errors: %d", - percent_passed (ts), ts->n_checked, ts->n_failed, ts->n_errors); - - return str; -} - -char * -ck_strdup_printf (const char *fmt, ...) -{ - /* Guess we need no more than 100 bytes. */ - int n; - size_t size = 100; - char *p; - va_list ap; - - p = (char *) emalloc (size); - - while (1) { - /* Try to print in the allocated space. */ - va_start (ap, fmt); - n = vsnprintf (p, size, fmt, ap); - va_end (ap); - /* If that worked, return the string. */ - if (n > -1 && n < (int) size) - return p; - - /* Else try again with more space. */ - if (n > -1) /* C99 conform vsnprintf() */ - size = (size_t) n + 1; /* precisely what is needed */ - else /* glibc 2.0 */ - size *= 2; /* twice the old size */ - - p = (char *) erealloc (p, size); - } -} - -static const char * -tr_type_str (TestResult * tr) -{ - const char *str = NULL; - - if (tr->ctx == CK_CTX_TEST) { - if (tr->rtype == CK_PASS) - str = "P"; - else if (tr->rtype == CK_FAILURE) - str = "F"; - else if (tr->rtype == CK_ERROR) - str = "E"; - } else - str = "S"; - - return str; -} - -static int -percent_passed (TestStats * t) -{ - if (t->n_failed == 0 && t->n_errors == 0) - return 100; - else if (t->n_checked == 0) - return 0; - else - return (int) ((float) (t->n_checked - (t->n_failed + t->n_errors)) / - (float) t->n_checked * 100); -} diff --git a/libs/gst/check/libcheck/check_str.h b/libs/gst/check/libcheck/check_str.h deleted file mode 100644 index f7fdba7107..0000000000 --- a/libs/gst/check/libcheck/check_str.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#ifndef CHECK_STR_H -#define CHECK_STR_H - -/* Return a string representation of the given TestResult. Return - value has been malloc'd, and must be freed by the caller */ -char *tr_str (TestResult * tr); - -/* Return a string representation of the given TestResult message - without the test id or result type. This is suitable for separate - formatting of the test and the message. Return value has been - malloc'd, and must be freed by the caller */ -char *tr_short_str (TestResult * tr); - -/* Return a string representation of the given SRunner's run - statistics (% passed, num run, passed, errors, failures). Return - value has been malloc'd, and must be freed by the caller -*/ -char *sr_stat_str (SRunner * sr); - -char *ck_strdup_printf (const char *fmt, ...); - -#endif /* CHECK_STR_H */ diff --git a/libs/gst/check/libcheck/libcompat/alarm.c b/libs/gst/check/libcheck/libcompat/alarm.c deleted file mode 100644 index a973f2219c..0000000000 --- a/libs/gst/check/libcheck/libcompat/alarm.c +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat.h" - -unsigned int -alarm (unsigned int seconds CK_ATTRIBUTE_UNUSED) -{ - assert (0); - return 0; -} diff --git a/libs/gst/check/libcheck/libcompat/clock_gettime.c b/libs/gst/check/libcheck/libcompat/clock_gettime.c deleted file mode 100644 index 51cfd8f9bf..0000000000 --- a/libs/gst/check/libcheck/libcompat/clock_gettime.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat.h" - -#ifdef __APPLE__ -#include <mach/clock.h> -#include <mach/mach.h> -#include <mach/mach_time.h> -#include <TargetConditionals.h> -/* CoreServices.h is only available on macOS */ -# if TARGET_OS_MAC && !TARGET_OS_IPHONE -# include <CoreServices/CoreServices.h> -# endif -#include <unistd.h> -#endif - -#define NANOSECONDS_PER_SECOND 1000000000 - - - -int -clock_gettime (clockid_t clk_id CK_ATTRIBUTE_UNUSED, struct timespec *ts) -{ - -#ifdef __APPLE__ - /* OS X does not have clock_gettime, use mach_absolute_time */ - - static mach_timebase_info_data_t sTimebaseInfo; - uint64_t rawTime; - uint64_t nanos; - - rawTime = mach_absolute_time (); - - /* - * OS X has a function to convert abs time to nano seconds: AbsoluteToNanoseconds - * However, the function may not be available as we may not have - * access to CoreServices. Because of this, we convert the abs time - * to nano seconds manually. - */ - - /* - * First grab the time base used on the system, if this is the first - * time we are being called. We can check if the value is uninitialized, - * as the denominator will be zero. - */ - if (sTimebaseInfo.denom == 0) { - (void) mach_timebase_info (&sTimebaseInfo); - } - - /* - * Do the conversion. We hope that the multiplication doesn't - * overflow; the price you pay for working in fixed point. - */ - nanos = rawTime * sTimebaseInfo.numer / sTimebaseInfo.denom; - - /* - * Fill in the timespec container - */ - ts->tv_sec = nanos / NANOSECONDS_PER_SECOND; - ts->tv_nsec = nanos - (ts->tv_sec * NANOSECONDS_PER_SECOND); -#else - /* - * As there is no function to fall back onto to get the current - * time, zero out the time so the caller will have a sane value. - */ - ts->tv_sec = 0; - ts->tv_nsec = 0; -#endif - - return 0; -} diff --git a/libs/gst/check/libcheck/libcompat/getline.c b/libs/gst/check/libcheck/libcompat/getline.c deleted file mode 100644 index edc073f1bd..0000000000 --- a/libs/gst/check/libcheck/libcompat/getline.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat.h" -#include <stdio.h> - -#define INITIAL_SIZE 16 -#define DELIMITER '\n' - -ssize_t -getline (char **lineptr, size_t * n, FILE * stream) -{ - ssize_t written = 0; - int character; - - if (*lineptr == NULL || *n < INITIAL_SIZE) { - free (*lineptr); - *lineptr = (char *) malloc (INITIAL_SIZE); - *n = INITIAL_SIZE; - } - - while ((character = fgetc (stream)) != EOF) { - written += 1; - if (written >= *n) { - *n = *n * 2; - *lineptr = realloc (*lineptr, *n); - } - - (*lineptr)[written - 1] = character; - - if (character == DELIMITER) { - break; - } - } - - (*lineptr)[written] = '\0'; - - return written; -} diff --git a/libs/gst/check/libcheck/libcompat/gettimeofday.c b/libs/gst/check/libcheck/libcompat/gettimeofday.c deleted file mode 100644 index 78b327e6df..0000000000 --- a/libs/gst/check/libcheck/libcompat/gettimeofday.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat.h" -#include <errno.h> - -#if defined(_MSC_VER) || defined(__BORLANDC__) -#define EPOCHFILETIME (116444736000000000i64) -#else -#define EPOCHFILETIME (116444736000000000LL) -#endif - -int -gettimeofday (struct timeval *tv, void *tz) -{ -#if _MSC_VER - union - { - __int64 ns100; /*time since 1 Jan 1601 in 100ns units */ - FILETIME ft; - } now; - - GetSystemTimeAsFileTime (&now.ft); - tv->tv_usec = (long) ((now.ns100 / 10LL) % 1000000LL); - tv->tv_sec = (long) ((now.ns100 - EPOCHFILETIME) / 10000000LL); - return (0); -#else - // Return that there is no implementation of this on the system - errno = ENOSYS; - return -1; -#endif /* _MSC_VER */ -} diff --git a/libs/gst/check/libcheck/libcompat/libcompat.c b/libs/gst/check/libcheck/libcompat/libcompat.c deleted file mode 100644 index fefcf38c61..0000000000 --- a/libs/gst/check/libcheck/libcompat/libcompat.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat.h" - -/* silence warnings about an empty library */ -void -ck_do_nothing (void) -{ - assert (0); - - /* - * to silence warning about this function actually - * returning, but being marked as noreturn. assert() - * must be marked as a function that returns. - */ - exit (1); -} diff --git a/libs/gst/check/libcheck/libcompat/libcompat.h b/libs/gst/check/libcheck/libcompat/libcompat.h deleted file mode 100644 index b88121bcf2..0000000000 --- a/libs/gst/check/libcheck/libcompat/libcompat.h +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#ifndef LIBCOMPAT_H -#define LIBCOMPAT_H - -#if HAVE_CONFIG_H -#include <config.h> -#endif - -#if defined(__GNUC__) && defined(__GNUC_MINOR__) -#define GCC_VERSION_AT_LEAST(major, minor) \ -((__GNUC__ > (major)) || \ - (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))) -#else -#define GCC_VERSION_AT_LEAST(major, minor) 0 -#endif - -#if GCC_VERSION_AT_LEAST(2,95) -#define CK_ATTRIBUTE_UNUSED __attribute__ ((unused)) -#else -#define CK_ATTRIBUTE_UNUSED -#endif /* GCC 2.95 */ - -#if GCC_VERSION_AT_LEAST(2,5) -#define CK_ATTRIBUTE_NORETURN __attribute__ ((noreturn)) -#else -#define CK_ATTRIBUTE_NORETURN -#endif /* GCC 2.5 */ - -/* - * Used for MSVC to create the export attribute - * CK_DLL_EXP is defined during the compilation of the library - * on the command line. - */ -#ifndef CK_DLL_EXP -#define CK_DLL_EXP extern -#endif - -#if _MSC_VER -#include <WinSock2.h> /* struct timeval, API used in gettimeofday implementation */ -#include <io.h> /* read, write */ -#include <process.h> /* getpid */ -#include <BaseTsd.h> /* for ssize_t */ -typedef SSIZE_T ssize_t; -#endif /* _MSC_VER */ - -/* defines size_t */ -#include <sys/types.h> - -/* provides assert */ -#include <assert.h> - -/* defines FILE */ -#include <stdio.h> - -/* defines exit() */ -#include <stdlib.h> - -/* provides localtime and struct tm */ -#ifdef HAVE_SYS_TIME_H -#include <sys/time.h> -#endif /* !HAVE_SYS_TIME_H */ -#include <time.h> - -/* declares fork(), _POSIX_VERSION. according to Autoconf.info, - unistd.h defines _POSIX_VERSION if the system is POSIX-compliant, - so we will use this as a test for all things uniquely provided by - POSIX like sigaction() and fork() */ -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif - -#ifdef HAVE_SYS_WAIT_H -#include <sys/wait.h> -#endif - -/* declares pthread_create and friends */ -#ifdef HAVE_PTHREAD -#include <pthread.h> -#endif - -#ifdef HAVE_STDINT_H -#include <stdint.h> -#endif - -/* replacement functions for broken originals */ -#if !HAVE_DECL_ALARM -CK_DLL_EXP unsigned int alarm (unsigned int seconds); -#endif /* !HAVE_DECL_ALARM */ - -#if !HAVE_MALLOC -CK_DLL_EXP void *rpl_malloc (size_t n); -#endif /* !HAVE_MALLOC */ - -#if !HAVE_REALLOC -CK_DLL_EXP void *rpl_realloc (void *p, size_t n); -#endif /* !HAVE_REALLOC */ - -#if !HAVE_GETPID && HAVE__GETPID -#define getpid _getpid -#endif /* !HAVE_GETPID && HAVE__GETPID */ - -#if !HAVE_GETTIMEOFDAY -CK_DLL_EXP int gettimeofday (struct timeval *tv, void *tz); -#endif /* !HAVE_GETTIMEOFDAY */ - -#if !HAVE_DECL_LOCALTIME_R -#if !defined(localtime_r) -CK_DLL_EXP struct tm *localtime_r (const time_t * clock, struct tm *result); -#endif -#endif /* !HAVE_DECL_LOCALTIME_R */ - -#if !HAVE_DECL_STRDUP && !HAVE__STRDUP -CK_DLL_EXP char *strdup (const char *str); -#elif !HAVE_DECL_STRDUP && HAVE__STRDUP -#define strdup _strdup -#endif /* !HAVE_DECL_STRDUP && HAVE__STRDUP */ - -#if !HAVE_DECL_STRSIGNAL -CK_DLL_EXP char *strsignal (int sig); -#endif /* !HAVE_DECL_STRSIGNAL */ - -/* - * On systems where clock_gettime() is not available, or - * on systems where some clocks may not be supported, the - * definition for CLOCK_MONOTONIC and CLOCK_REALTIME may not - * be available. These should define which type of clock - * clock_gettime() should use. We define it here if it is - * not defined simply so the reimplementation can ignore it. - * - * We set the values of these clocks to some (hopefully) - * invalid value, to avoid the case where we define a - * clock with a valid value, and unintentionally use - * an actual good clock by accident. - */ -#ifndef CLOCK_MONOTONIC -#define CLOCK_MONOTONIC -1 -#endif -#ifndef CLOCK_REALTIME -#define CLOCK_REALTIME -1 -#endif - -#ifndef HAVE_LIBRT - -#ifdef STRUCT_TIMESPEC_DEFINITION_MISSING -/* - * The following structure is defined in POSIX 1003.1 for times - * specified in seconds and nanoseconds. If it is not defined in - * time.g, then we need to define it here - */ -struct timespec -{ - time_t tv_sec; - long tv_nsec; -}; -#endif /* STRUCT_TIMESPEC_DEFINITION_MISSING */ - -#ifdef STRUCT_ITIMERSPEC_DEFINITION_MISSING -/* - * The following structure is defined in POSIX.1b for timer start values and intervals. - * If it is not defined in time.h, then we need to define it here. - */ -struct itimerspec -{ - struct timespec it_interval; - struct timespec it_value; -}; -#endif /* STRUCT_ITIMERSPEC_DEFINITION_MISSING */ - -/* - * Do a simple forward declaration in case the struct is not defined. - * In the versions of timer_create in libcompat, sigevent is never - * used. - */ -struct sigevent; - -#ifndef HAVE_CLOCK_GETTIME -CK_DLL_EXP int clock_gettime (clockid_t clk_id, struct timespec *ts); -#endif -CK_DLL_EXP int timer_create (clockid_t clockid, struct sigevent *sevp, - timer_t * timerid); -CK_DLL_EXP int timer_settime (timer_t timerid, int flags, - const struct itimerspec *new_value, struct itimerspec *old_value); -CK_DLL_EXP int timer_delete (timer_t timerid); -#endif /* HAVE_LIBRT */ - -/* - * The following checks are to determine if the system's - * snprintf (or its variants) should be replaced with - * the C99 compliant version in libcompat. - */ -#if HAVE_CONFIG_H -#include <config.h> -#endif -#if HAVE_STDARG_H -#include <stdarg.h> - -#if !HAVE_VSNPRINTF -CK_DLL_EXP int rpl_vsnprintf (char *, size_t, const char *, va_list); - -#define vsnprintf rpl_vsnprintf -#endif -#if !HAVE_SNPRINTF -CK_DLL_EXP int rpl_snprintf (char *, size_t, const char *, ...); - -#define snprintf rpl_snprintf -#endif -#endif /* HAVE_STDARG_H */ - -#if !HAVE_GETLINE -CK_DLL_EXP ssize_t getline (char **lineptr, size_t * n, FILE * stream); -#endif - -/* silence warnings about an empty library */ -CK_DLL_EXP void -ck_do_nothing (void) - CK_ATTRIBUTE_NORETURN; - -#endif /* !LIBCOMPAT_H */ diff --git a/libs/gst/check/libcheck/libcompat/localtime_r.c b/libs/gst/check/libcheck/libcompat/localtime_r.c deleted file mode 100644 index 06ac582f86..0000000000 --- a/libs/gst/check/libcheck/libcompat/localtime_r.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat.h" - -#if !defined(localtime_r) - -struct tm * -localtime_r (const time_t * clock, struct tm *result) -{ - struct tm *now = localtime (clock); - - if (now == NULL) { - return NULL; - } else { - *result = *now; - } - - return result; -} - -#endif /* !defined(localtime_r) */ diff --git a/libs/gst/check/libcheck/libcompat/strdup.c b/libs/gst/check/libcheck/libcompat/strdup.c deleted file mode 100644 index 1d6aa61a81..0000000000 --- a/libs/gst/check/libcheck/libcompat/strdup.c +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat.h" - -char * -strdup (const char *str CK_ATTRIBUTE_UNUSED) -{ - assert (0); - return NULL; -} diff --git a/libs/gst/check/libcheck/libcompat/strsignal.c b/libs/gst/check/libcheck/libcompat/strsignal.c deleted file mode 100644 index fd93ad5037..0000000000 --- a/libs/gst/check/libcheck/libcompat/strsignal.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat.h" - -char * -strsignal (int sig) -{ - static char signame[40]; - - sprintf (signame, "SIG #%d", sig); - return signame; -} diff --git a/libs/gst/check/libcheck/libcompat/timer_create.c b/libs/gst/check/libcheck/libcompat/timer_create.c deleted file mode 100644 index 2e250f0d08..0000000000 --- a/libs/gst/check/libcheck/libcompat/timer_create.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat.h" - -int -timer_create (clockid_t clockid CK_ATTRIBUTE_UNUSED, - struct sigevent *sevp CK_ATTRIBUTE_UNUSED, - timer_t * timerid CK_ATTRIBUTE_UNUSED) -{ - /* - * The create function does nothing. timer_settime will use - * alarm to set the timer, and timer_delete will stop the - * alarm - */ - - return 0; -} diff --git a/libs/gst/check/libcheck/libcompat/timer_delete.c b/libs/gst/check/libcheck/libcompat/timer_delete.c deleted file mode 100644 index 5740bd65dc..0000000000 --- a/libs/gst/check/libcheck/libcompat/timer_delete.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat.h" - -int -timer_delete (timer_t timerid CK_ATTRIBUTE_UNUSED) -{ -#ifdef HAVE_SETITIMER - /* - * If the system does not have timer_settime() but does have - * setitimer() use that instead of alarm(). - */ - struct itimerval interval; - - /* - * Setting values to '0' results in disabling the running timer. - */ - interval.it_value.tv_sec = 0; - interval.it_value.tv_usec = 0; - interval.it_interval.tv_sec = 0; - interval.it_interval.tv_usec = 0; - - return setitimer (ITIMER_REAL, &interval, NULL); -#else - /* - * There is only one timer, that used by alarm. - * Setting alarm(0) will not set a new alarm, and - * will kill the previous timer. - */ - - alarm (0); - - return 0; -#endif -} diff --git a/libs/gst/check/libcheck/libcompat/timer_settime.c b/libs/gst/check/libcheck/libcompat/timer_settime.c deleted file mode 100644 index db2b1d597a..0000000000 --- a/libs/gst/check/libcheck/libcompat/timer_settime.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Check: a unit test framework for C - * Copyright (C) 2001, 2002 Arien Malec - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include "libcompat.h" - -int -timer_settime (timer_t timerid CK_ATTRIBUTE_UNUSED, - int flags CK_ATTRIBUTE_UNUSED, - const struct itimerspec *new_value, - struct itimerspec *old_value CK_ATTRIBUTE_UNUSED) -{ -#ifdef HAVE_SETITIMER - /* - * If the system does not have timer_settime() but does have - * setitimer() use that instead of alarm(). - */ - struct itimerval interval; - - interval.it_value.tv_sec = new_value->it_value.tv_sec; - interval.it_value.tv_usec = new_value->it_value.tv_nsec / 1000; - interval.it_interval.tv_sec = new_value->it_interval.tv_sec; - interval.it_interval.tv_usec = new_value->it_interval.tv_nsec / 1000; - - return setitimer (ITIMER_REAL, &interval, NULL); -#else - int seconds = new_value->it_value.tv_sec; - - /* - * As the alarm() call has only second precision, if the caller - * specifies partial seconds, we round up to the nearest second. - */ - if (new_value->it_value.tv_nsec > 0) { - seconds += 1; - } - - alarm (seconds); - - return 0; -#endif -} diff --git a/libs/gst/check/libcheck/meson.build b/libs/gst/check/libcheck/meson.build deleted file mode 100644 index c6d496d182..0000000000 --- a/libs/gst/check/libcheck/meson.build +++ /dev/null @@ -1,91 +0,0 @@ -libcheck_files = [ - 'check.c', - 'check_error.c', - 'check_list.c', - 'check_log.c', - 'check_msg.c', - 'check_pack.c', - 'check_print.c', - 'check_run.c', - 'check_str.c', - 'libcompat/libcompat.c' -] - -if not cdata.has('HAVE_ALARM') - libcheck_files += ['libcompat/alarm.c'] -endif - -if not cdata.has('HAVE_GETTIMEOFDAY') - libcheck_files += ['libcompat/gettimeofday.c'] -endif - -if not cdata.has('HAVE_CLOCK_GETTIME') - libcheck_files += ['libcompat/clock_gettime.c'] -endif - -if not cdata.has('HAVE_DECL_LOCALTIME_R') - libcheck_files += ['libcompat/localtime_r.c'] -endif - -if not cdata.has('HAVE_DECL_STRSIGNAL') - libcheck_files += ['libcompat/strsignal.c'] -endif - -if not cdata.has('HAVE_DECL_STRDUP') and not cdata.has('HAVE__STRDUP') - libcheck_files += ['libcompat/strdup.c'] -endif - -if not cdata.has('HAVE_GETLINE') - libcheck_files += ['libcompat/getline.c'] -endif - -# FIXME: check that timer_create, timer_settime, timer_delete are in rt_lib -if not rt_lib.found() - libcheck_files += [ - 'libcompat/timer_create.c', - 'libcompat/timer_settime.c', - 'libcompat/timer_delete.c' - ] -endif - -configure_file(input : 'check.h.in', - output : 'check.h', - configuration : check_cdata) - -internal_check_h_inc = include_directories('..') - -# Must explicitly make symbols public if default visibility is hidden -if have_visibility_hidden - libcheck_visibility_args = ['-DCK_DLL_EXP=extern __attribute__ ((visibility ("default")))'] -else - if host_system == 'windows' - libcheck_visibility_args = ['-DCK_DLL_EXP=__declspec(dllexport)'] - else - libcheck_visibility_args = ['-DCK_DLL_EXP=extern'] - endif -endif - -no_warn_args = [] -foreach arg : [ - '-Wno-undef', - '-Wno-redundant-decls', - '-Wno-missing-prototypes', - '-Wno-missing-declarations', - '-Wno-old-style-definition', - '-Wno-declaration-after-statement', - '-Wno-format-nonliteral', - '-Wno-tautological-constant-out-of-range-compare'] - if cc.has_argument(arg) - no_warn_args += [arg] - endif -endforeach - -libcheck = static_library('check', - libcheck_files, - include_directories : [configinc, internal_check_h_inc], - dependencies : [rt_lib, mathlib, glib_dep], - c_args: gst_c_args + libcheck_visibility_args + no_warn_args + - # Don't want libcompat to think we don't have these and substitute - # replacements since we don't check for or define these. See libcompat.h - ['-DHAVE_VSNPRINTF', '-DHAVE_SNPRINTF', '-DHAVE_MALLOC', '-DHAVE_REALLOC'], - pic: true) diff --git a/libs/gst/check/meson.build b/libs/gst/check/meson.build deleted file mode 100644 index 316465a350..0000000000 --- a/libs/gst/check/meson.build +++ /dev/null @@ -1,88 +0,0 @@ -gst_check_sources = [ - 'gstbufferstraw.c', - 'gstcheck.c', - 'gstconsistencychecker.c', - 'gstharness.c', - 'gsttestclock.c', -] -gst_check_headers = [ - 'check.h', - 'check-prelude.h', - 'gstbufferstraw.h', - 'gstcheck.h', - 'gstconsistencychecker.h', - 'gstharness.h', - 'gsttestclock.h', -] -install_headers(gst_check_headers, subdir : 'gstreamer-1.0/gst/check/') - - -check_cdata = configuration_data() - -check_cdata.set('ENABLE_SUBUNIT', 0) -check_cdata.set('CHECK_MAJOR_VERSION', 0) -check_cdata.set('CHECK_MINOR_VERSION', 9) -check_cdata.set('CHECK_MICRO_VERSION', 14) -if host_system != 'windows' - check_cdata.set('HAVE_FORK', 1) -else - check_cdata.set('HAVE_FORK', 0) -endif - -subdir('libcheck') - -configure_file(input : 'libcheck/check.h.in', - output : 'internal-check.h', - install_dir : join_paths(get_option('includedir'), 'gstreamer-1.0/gst/check'), - configuration : check_cdata) - -gst_check = library('gstcheck-@0@'.format(apiversion), - gst_check_sources, - c_args : gst_c_args + ['-UG_DISABLE_ASSERT', '-DBUILDING_GST_CHECK'], - version : libversion, - soversion : soversion, - darwin_versions : osxversion, - install : true, - include_directories : [configinc, libsinc], - link_with : [libcheck], - dependencies : [gobject_dep, glib_dep, gst_dep], -) - -pkgconfig.generate(gst_check, - libraries : [libgst], - # FIXME: Add manually libcheck's dependencies because it's an uninstalled static - # library and Meson <0.56.0 does not handle them correctly. - # See https://github.com/mesonbuild/meson/pull/7488. - libraries_private: [rt_lib, mathlib], - variables : pkgconfig_variables, - subdirs : pkgconfig_subdirs, - name : 'gstreamer-check-1.0', - description : 'Unit testing helper library for GStreamer modules', -) - -gst_check_gen_sources = [] - -if build_gir - gst_gir_extra_args = gir_init_section + [ '--c-include=gst/check/check.h' ] - gst_check_gir = gnome.generate_gir(gst_check, - sources : gst_check_sources + gst_check_headers, - namespace : 'GstCheck', - nsversion : apiversion, - identifier_prefix : 'Gst', - symbol_prefix : 'gst', - export_packages : 'gstreamer-check-1.0', - dependencies : [gst_dep], - include_directories : [configinc, libsinc, privinc], - includes : ['GLib-2.0', 'GObject-2.0', 'GModule-2.0', 'Gst-1.0'], - install : true, - extra_args : gst_gir_extra_args, - ) - gst_check_gen_sources += gst_check_gir -endif - -gst_check_dep = declare_dependency(link_with : gst_check, - include_directories : [libsinc], - dependencies : [gst_dep], - sources : gst_check_gen_sources) - -meson.override_dependency('gstreamer-check-1.0', gst_check_dep) diff --git a/libs/gst/controller/controller-prelude.h b/libs/gst/controller/controller-prelude.h deleted file mode 100644 index cd7f0c9196..0000000000 --- a/libs/gst/controller/controller-prelude.h +++ /dev/null @@ -1,35 +0,0 @@ -/* GStreamer Controller Library - * Copyright (C) 2018 GStreamer developers - * - * controller-prelude.h: prelude include header for gst-controller library - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_CONTROLLER_PRELUDE_H__ -#define __GST_CONTROLLER_PRELUDE_H__ - -#include <gst/gst.h> - -#ifndef GST_CONTROLLER_API -#ifdef BUILDING_GST_CONTROLLER -#define GST_CONTROLLER_API GST_API_EXPORT /* from config.h */ -#else -#define GST_CONTROLLER_API GST_API_IMPORT -#endif -#endif - -#endif /* __GST_CONTROLLER_PRELUDE_H__ */ diff --git a/libs/gst/controller/controller.h b/libs/gst/controller/controller.h deleted file mode 100644 index 79ae8ce6da..0000000000 --- a/libs/gst/controller/controller.h +++ /dev/null @@ -1,35 +0,0 @@ -/* GStreamer - * Copyright (C) 2012 GStreamer developers - * - * gstcontroller.h: single include header for gst-controller library - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_CONTROLLER_H__ -#define __GST_CONTROLLER_H__ - -#include <gst/controller/controller-prelude.h> - -#include <gst/controller/gstargbcontrolbinding.h> -#include <gst/controller/gstdirectcontrolbinding.h> -#include <gst/controller/gstproxycontrolbinding.h> -#include <gst/controller/gsttimedvaluecontrolsource.h> -#include <gst/controller/gstinterpolationcontrolsource.h> -#include <gst/controller/gsttriggercontrolsource.h> -#include <gst/controller/gstlfocontrolsource.h> - -#endif /* __GST_CONTROLLER_H__ */ diff --git a/libs/gst/controller/gstargbcontrolbinding.c b/libs/gst/controller/gstargbcontrolbinding.c deleted file mode 100644 index b8014a0db9..0000000000 --- a/libs/gst/controller/gstargbcontrolbinding.c +++ /dev/null @@ -1,486 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2011 Stefan Sauer <ensonic@users.sf.net> - * - * gstargbcontrolbinding.c: Attachment for multiple control sources to gargb - * properties - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ -/** - * SECTION:gstargbcontrolbinding - * @title: GstARGBControlBinding - * @short_description: attachment for control sources to argb properties - * - * A value mapping object that attaches multiple control sources to a guint - * gobject properties representing a color. A control value of 0.0 will turn the - * color component off and a value of 1.0 will be the color level. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <glib-object.h> -#include <gst/gst.h> - -#include "gstargbcontrolbinding.h" - -#include <gst/math-compat.h> - -#define GST_CAT_DEFAULT control_binding_debug -GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); - -static GObject *gst_argb_control_binding_constructor (GType type, - guint n_construct_params, GObjectConstructParam * construct_params); -static void gst_argb_control_binding_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_argb_control_binding_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); -static void gst_argb_control_binding_dispose (GObject * object); -static void gst_argb_control_binding_finalize (GObject * object); - -static gboolean gst_argb_control_binding_sync_values (GstControlBinding * _self, - GstObject * object, GstClockTime timestamp, GstClockTime last_sync); -static GValue *gst_argb_control_binding_get_value (GstControlBinding * _self, - GstClockTime timestamp); -static gboolean gst_argb_control_binding_get_value_array (GstControlBinding * - _self, GstClockTime timestamp, GstClockTime interval, guint n_values, - gpointer values); -static gboolean gst_argb_control_binding_get_g_value_array (GstControlBinding * - _self, GstClockTime timestamp, GstClockTime interval, guint n_values, - GValue * values); - -#define _do_init \ - GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gstargbcontrolbinding", 0, \ - "dynamic parameter control source attachment"); - -#define gst_argb_control_binding_parent_class parent_class -G_DEFINE_TYPE_WITH_CODE (GstARGBControlBinding, gst_argb_control_binding, - GST_TYPE_CONTROL_BINDING, _do_init); - -enum -{ - PROP_0, - PROP_CS_A, - PROP_CS_R, - PROP_CS_G, - PROP_CS_B, - PROP_LAST -}; - -static GParamSpec *properties[PROP_LAST]; - -/* vmethods */ - -static void -gst_argb_control_binding_class_init (GstARGBControlBindingClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GstControlBindingClass *control_binding_class = - GST_CONTROL_BINDING_CLASS (klass); - - gobject_class->constructor = gst_argb_control_binding_constructor; - gobject_class->set_property = gst_argb_control_binding_set_property; - gobject_class->get_property = gst_argb_control_binding_get_property; - gobject_class->dispose = gst_argb_control_binding_dispose; - gobject_class->finalize = gst_argb_control_binding_finalize; - - control_binding_class->sync_values = gst_argb_control_binding_sync_values; - control_binding_class->get_value = gst_argb_control_binding_get_value; - control_binding_class->get_value_array = - gst_argb_control_binding_get_value_array; - control_binding_class->get_g_value_array = - gst_argb_control_binding_get_g_value_array; - - properties[PROP_CS_A] = - g_param_spec_object ("control-source-a", "ControlSource A", - "The control source for the alpha color component", - GST_TYPE_CONTROL_SOURCE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); - - properties[PROP_CS_R] = - g_param_spec_object ("control-source-r", "ControlSource R", - "The control source for the red color component", - GST_TYPE_CONTROL_SOURCE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); - - properties[PROP_CS_G] = - g_param_spec_object ("control-source-g", "ControlSource G", - "The control source for the green color component", - GST_TYPE_CONTROL_SOURCE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); - - properties[PROP_CS_B] = - g_param_spec_object ("control-source-b", "ControlSource B", - "The control source for the blue color component", - GST_TYPE_CONTROL_SOURCE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (gobject_class, PROP_LAST, properties); -} - -static void -gst_argb_control_binding_init (GstARGBControlBinding * self) -{ -} - -static GObject * -gst_argb_control_binding_constructor (GType type, guint n_construct_params, - GObjectConstructParam * construct_params) -{ - GstARGBControlBinding *self; - - self = - GST_ARGB_CONTROL_BINDING (G_OBJECT_CLASS (parent_class)->constructor - (type, n_construct_params, construct_params)); - - if (GST_CONTROL_BINDING_PSPEC (self)) { - if (!(G_PARAM_SPEC_VALUE_TYPE (GST_CONTROL_BINDING_PSPEC (self)) == - G_TYPE_UINT)) { - GST_WARNING ("can't bind to paramspec type '%s'", - G_PARAM_SPEC_TYPE_NAME (GST_CONTROL_BINDING_PSPEC (self))); - GST_CONTROL_BINDING_PSPEC (self) = NULL; - } else { - g_value_init (&self->cur_value, G_TYPE_UINT); - } - } - return (GObject *) self; -} - -static void -gst_argb_control_binding_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstARGBControlBinding *self = GST_ARGB_CONTROL_BINDING (object); - - switch (prop_id) { - case PROP_CS_A: - gst_object_replace ((GstObject **) & self->cs_a, - g_value_get_object (value)); - break; - case PROP_CS_R: - gst_object_replace ((GstObject **) & self->cs_r, - g_value_get_object (value)); - break; - case PROP_CS_G: - gst_object_replace ((GstObject **) & self->cs_g, - g_value_get_object (value)); - break; - case PROP_CS_B: - gst_object_replace ((GstObject **) & self->cs_b, - g_value_get_object (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_argb_control_binding_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstARGBControlBinding *self = GST_ARGB_CONTROL_BINDING (object); - - switch (prop_id) { - case PROP_CS_A: - g_value_set_object (value, self->cs_a); - break; - case PROP_CS_R: - g_value_set_object (value, self->cs_r); - break; - case PROP_CS_G: - g_value_set_object (value, self->cs_g); - break; - case PROP_CS_B: - g_value_set_object (value, self->cs_b); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_argb_control_binding_dispose (GObject * object) -{ - GstARGBControlBinding *self = GST_ARGB_CONTROL_BINDING (object); - - gst_object_replace ((GstObject **) & self->cs_a, NULL); - gst_object_replace ((GstObject **) & self->cs_r, NULL); - gst_object_replace ((GstObject **) & self->cs_g, NULL); - gst_object_replace ((GstObject **) & self->cs_b, NULL); - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -gst_argb_control_binding_finalize (GObject * object) -{ - GstARGBControlBinding *self = GST_ARGB_CONTROL_BINDING (object); - - g_value_unset (&self->cur_value); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static gboolean -gst_argb_control_binding_sync_values (GstControlBinding * _self, - GstObject * object, GstClockTime timestamp, GstClockTime last_sync) -{ - GstARGBControlBinding *self = GST_ARGB_CONTROL_BINDING (_self); - gdouble src_val_a = 1.0, src_val_r = 0.0, src_val_g = 0.0, src_val_b = 0.0; - gboolean ret = TRUE; - - g_return_val_if_fail (GST_IS_ARGB_CONTROL_BINDING (self), FALSE); - g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE); - - GST_LOG_OBJECT (object, "property '%s' at ts=%" GST_TIME_FORMAT, - _self->name, GST_TIME_ARGS (timestamp)); - - if (self->cs_a) - ret &= gst_control_source_get_value (self->cs_a, timestamp, &src_val_a); - if (self->cs_r) - ret &= gst_control_source_get_value (self->cs_r, timestamp, &src_val_r); - if (self->cs_g) - ret &= gst_control_source_get_value (self->cs_g, timestamp, &src_val_g); - if (self->cs_b) - ret &= gst_control_source_get_value (self->cs_b, timestamp, &src_val_b); - if (G_LIKELY (ret)) { - guint src_val = (((guint) (CLAMP (src_val_a, 0.0, 1.0) * 255)) << 24) | - (((guint) (CLAMP (src_val_r, 0.0, 1.0) * 255)) << 16) | - (((guint) (CLAMP (src_val_g, 0.0, 1.0) * 255)) << 8) | - ((guint) (CLAMP (src_val_b, 0.0, 1.0) * 255)); - GST_LOG_OBJECT (object, " new value 0x%08x", src_val); - /* always set the value for first time, but then only if it changed - * this should limit g_object_notify invocations. - * FIXME: can we detect negative playback rates? - */ - if ((timestamp < last_sync) || (src_val != self->last_value)) { - GValue *dst_val = &self->cur_value; - - g_value_set_uint (dst_val, src_val); - /* we can make this faster - * http://bugzilla.gnome.org/show_bug.cgi?id=536939 - */ - g_object_set_property ((GObject *) object, _self->name, dst_val); - self->last_value = src_val; - } - } else { - GST_DEBUG_OBJECT (object, "no control value for param %s", _self->name); - } - return (ret); -} - -static GValue * -gst_argb_control_binding_get_value (GstControlBinding * _self, - GstClockTime timestamp) -{ - GstARGBControlBinding *self = GST_ARGB_CONTROL_BINDING (_self); - GValue *dst_val = NULL; - gdouble src_val_a = 1.0, src_val_r = 0.0, src_val_g = 0.0, src_val_b = 0.0; - gboolean ret = TRUE; - - g_return_val_if_fail (GST_IS_ARGB_CONTROL_BINDING (self), NULL); - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL); - g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE); - - /* get current value via control source */ - if (self->cs_a) - ret &= gst_control_source_get_value (self->cs_a, timestamp, &src_val_a); - if (self->cs_r) - ret &= gst_control_source_get_value (self->cs_r, timestamp, &src_val_r); - if (self->cs_g) - ret &= gst_control_source_get_value (self->cs_g, timestamp, &src_val_g); - if (self->cs_b) - ret &= gst_control_source_get_value (self->cs_b, timestamp, &src_val_b); - if (G_LIKELY (ret)) { - guint src_val = (((guint) (CLAMP (src_val_a, 0.0, 1.0) * 255)) << 24) | - (((guint) (CLAMP (src_val_r, 0.0, 1.0) * 255)) << 16) | - (((guint) (CLAMP (src_val_g, 0.0, 1.0) * 255)) << 8) | - ((guint) (CLAMP (src_val_b, 0.0, 1.0) * 255)); - dst_val = g_new0 (GValue, 1); - g_value_init (dst_val, G_TYPE_UINT); - g_value_set_uint (dst_val, src_val); - } else { - GST_LOG ("no control value for property %s at ts %" GST_TIME_FORMAT, - _self->name, GST_TIME_ARGS (timestamp)); - } - - return dst_val; -} - -static gboolean -gst_argb_control_binding_get_value_array (GstControlBinding * _self, - GstClockTime timestamp, GstClockTime interval, guint n_values, - gpointer values_) -{ - GstARGBControlBinding *self = GST_ARGB_CONTROL_BINDING (_self); - gint i; - gdouble *src_val_a = NULL, *src_val_r = NULL, *src_val_g = NULL, *src_val_b = - NULL; - guint *values = (guint *) values_; - gboolean ret = TRUE; - - g_return_val_if_fail (GST_IS_ARGB_CONTROL_BINDING (self), FALSE); - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), FALSE); - g_return_val_if_fail (values, FALSE); - g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE); - - if (self->cs_a) { - src_val_a = g_new0 (gdouble, n_values); - ret &= gst_control_source_get_value_array (self->cs_a, timestamp, - interval, n_values, src_val_a); - } - if (self->cs_r) { - src_val_r = g_new0 (gdouble, n_values); - ret &= gst_control_source_get_value_array (self->cs_r, timestamp, - interval, n_values, src_val_r); - } - if (self->cs_g) { - src_val_g = g_new0 (gdouble, n_values); - ret &= gst_control_source_get_value_array (self->cs_g, timestamp, - interval, n_values, src_val_g); - } - if (self->cs_b) { - src_val_b = g_new0 (gdouble, n_values); - ret &= gst_control_source_get_value_array (self->cs_b, timestamp, - interval, n_values, src_val_b); - } - if (G_LIKELY (ret)) { - for (i = 0; i < n_values; i++) { - gdouble a = 1.0, r = 0.0, g = 0.0, b = 0.0; - if (src_val_a && !isnan (src_val_a[i])) - a = src_val_a[i]; - if (src_val_r && !isnan (src_val_r[i])) - r = src_val_r[i]; - if (src_val_g && !isnan (src_val_g[i])) - g = src_val_g[i]; - if (src_val_b && !isnan (src_val_b[i])) - b = src_val_b[i]; - values[i] = (((guint) (CLAMP (a, 0.0, 1.0) * 255)) << 24) | - (((guint) (CLAMP (r, 0.0, 1.0) * 255)) << 16) | - (((guint) (CLAMP (g, 0.0, 1.0) * 255)) << 8) | - ((guint) (CLAMP (b, 0.0, 1.0) * 255)); - } - } else { - GST_LOG ("failed to get control value for property %s at ts %" - GST_TIME_FORMAT, _self->name, GST_TIME_ARGS (timestamp)); - } - g_free (src_val_a); - g_free (src_val_r); - g_free (src_val_g); - g_free (src_val_b); - return ret; -} - -static gboolean -gst_argb_control_binding_get_g_value_array (GstControlBinding * _self, - GstClockTime timestamp, GstClockTime interval, guint n_values, - GValue * values) -{ - GstARGBControlBinding *self = GST_ARGB_CONTROL_BINDING (_self); - gint i; - gdouble *src_val_a = NULL, *src_val_r = NULL, *src_val_g = NULL, *src_val_b = - NULL; - guint src_val; - gboolean ret = TRUE; - - g_return_val_if_fail (GST_IS_ARGB_CONTROL_BINDING (self), FALSE); - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), FALSE); - g_return_val_if_fail (values, FALSE); - g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE); - - if (self->cs_a) { - src_val_a = g_new0 (gdouble, n_values); - ret &= gst_control_source_get_value_array (self->cs_a, timestamp, - interval, n_values, src_val_a); - } - if (self->cs_r) { - src_val_r = g_new0 (gdouble, n_values); - ret &= gst_control_source_get_value_array (self->cs_r, timestamp, - interval, n_values, src_val_r); - } - if (self->cs_g) { - src_val_g = g_new0 (gdouble, n_values); - ret &= gst_control_source_get_value_array (self->cs_g, timestamp, - interval, n_values, src_val_g); - } - if (self->cs_b) { - src_val_b = g_new0 (gdouble, n_values); - ret &= gst_control_source_get_value_array (self->cs_b, timestamp, - interval, n_values, src_val_b); - } - if (G_LIKELY (ret)) { - for (i = 0; i < n_values; i++) { - gdouble a = 1.0, r = 0.0, g = 0.0, b = 0.0; - if (src_val_a && !isnan (src_val_a[i])) - a = src_val_a[i]; - if (src_val_r && !isnan (src_val_r[i])) - r = src_val_r[i]; - if (src_val_g && !isnan (src_val_g[i])) - g = src_val_g[i]; - if (src_val_b && !isnan (src_val_b[i])) - b = src_val_b[i]; - src_val = (((guint) (CLAMP (a, 0.0, 1.0) * 255)) << 24) | - (((guint) (CLAMP (r, 0.0, 1.0) * 255)) << 16) | - (((guint) (CLAMP (g, 0.0, 1.0) * 255)) << 8) | - ((guint) (CLAMP (b, 0.0, 1.0) * 255)); - g_value_init (&values[i], G_TYPE_UINT); - g_value_set_uint (&values[i], src_val); - } - } else { - GST_LOG ("failed to get control value for property %s at ts %" - GST_TIME_FORMAT, _self->name, GST_TIME_ARGS (timestamp)); - } - g_free (src_val_a); - g_free (src_val_r); - g_free (src_val_g); - g_free (src_val_b); - return ret; -} - -/* functions */ - -/** - * gst_argb_control_binding_new: - * @object: the object of the property - * @property_name: the property-name to attach the control source - * @cs_a: the control source for the alpha channel - * @cs_r: the control source for the red channel - * @cs_g: the control source for the green channel - * @cs_b: the control source for the blue channel - * - * Create a new control-binding that attaches the given #GstControlSource to the - * #GObject property. - * - * Returns: (transfer floating): the new #GstARGBControlBinding - */ -GstControlBinding * -gst_argb_control_binding_new (GstObject * object, const gchar * property_name, - GstControlSource * cs_a, GstControlSource * cs_r, GstControlSource * cs_g, - GstControlSource * cs_b) -{ - return (GstControlBinding *) g_object_new (GST_TYPE_ARGB_CONTROL_BINDING, - "object", object, "name", property_name, - "control-source-a", cs_a, - "control-source-r", cs_r, - "control-source-g", cs_g, "control-source-b", cs_b, NULL); -} - -/* functions */ diff --git a/libs/gst/controller/gstargbcontrolbinding.h b/libs/gst/controller/gstargbcontrolbinding.h deleted file mode 100644 index 3d1c8baacd..0000000000 --- a/libs/gst/controller/gstargbcontrolbinding.h +++ /dev/null @@ -1,103 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2011 Stefan Sauer <ensonic@users.sf.net> - * - * gstargbcontrolbinding.h: Attachment for multiple control sources to gargb - * properties - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_ARGB_CONTROL_BINDING_H__ -#define __GST_ARGB_CONTROL_BINDING_H__ - -#include <gst/gstconfig.h> - -#include <glib-object.h> - -#include <gst/gstcontrolsource.h> -#include <gst/controller/controller-prelude.h> - -G_BEGIN_DECLS - -#define GST_TYPE_ARGB_CONTROL_BINDING \ - (gst_argb_control_binding_get_type()) -#define GST_ARGB_CONTROL_BINDING(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ARGB_CONTROL_BINDING,GstARGBControlBinding)) -#define GST_ARGB_CONTROL_BINDING_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ARGB_CONTROL_BINDING,GstARGBControlBindingClass)) -#define GST_IS_ARGB_CONTROL_BINDING(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ARGB_CONTROL_BINDING)) -#define GST_IS_ARGB_CONTROL_BINDING_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ARGB_CONTROL_BINDING)) -#define GST_ARGB_CONTROL_BINDING_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CONTOL_SOURCE, GstARGBControlBindingClass)) - -typedef struct _GstARGBControlBinding GstARGBControlBinding; -typedef struct _GstARGBControlBindingClass GstARGBControlBindingClass; - -/** - * GstARGBControlBinding: - * @name: name of the property of this binding - * - * The instance structure of #GstARGBControlBinding. - */ -struct _GstARGBControlBinding { - GstControlBinding parent; - - /*< private >*/ - GstControlSource *cs_a; /* GstControlSources for this property */ - GstControlSource *cs_r; - GstControlSource *cs_g; - GstControlSource *cs_b; - - GValue cur_value; - guint32 last_value; - - gpointer _gst_reserved[GST_PADDING]; -}; - -/** - * GstARGBControlBindingClass: - * @parent_class: Parent class - * @convert: Class method to convert control-values - * - * The class structure of #GstARGBControlBinding. - */ - -struct _GstARGBControlBindingClass -{ - GstControlBindingClass parent_class; - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; -}; - -GST_CONTROLLER_API -GType gst_argb_control_binding_get_type (void); - -/* Functions */ - -GST_CONTROLLER_API -GstControlBinding * gst_argb_control_binding_new (GstObject * object, const gchar * property_name, - GstControlSource * cs_a, GstControlSource * cs_r, - GstControlSource * cs_g, GstControlSource * cs_b); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstARGBControlBinding, gst_object_unref) - -G_END_DECLS - -#endif /* __GST_ARGB_CONTROL_BINDING_H__ */ diff --git a/libs/gst/controller/gstdirectcontrolbinding.c b/libs/gst/controller/gstdirectcontrolbinding.c deleted file mode 100644 index 0c4d589f4b..0000000000 --- a/libs/gst/controller/gstdirectcontrolbinding.c +++ /dev/null @@ -1,553 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2011 Stefan Sauer <ensonic@users.sf.net> - * - * gstdirectcontrolbinding.c: Direct attachment for control sources - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ -/** - * SECTION:gstdirectcontrolbinding - * @title: GstDirectControlBinding - * @short_description: direct attachment for control sources - * - * A value mapping object that attaches control sources to gobject properties. It - * will map the control values directly to the target property range. If a - * non-absolute direct control binding is used, the value range [0.0 ... 1.0] - * is mapped to full target property range, and all values outside the range - * will be clipped. An absolute control binding will not do any value - * transformations. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <glib-object.h> -#include <gst/gst.h> - -#include "gstdirectcontrolbinding.h" - -#include <gst/math-compat.h> - -#define GST_CAT_DEFAULT control_binding_debug -GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); - - -static GObject *gst_direct_control_binding_constructor (GType type, - guint n_construct_params, GObjectConstructParam * construct_params); -static void gst_direct_control_binding_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_direct_control_binding_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); -static void gst_direct_control_binding_dispose (GObject * object); -static void gst_direct_control_binding_finalize (GObject * object); - -static gboolean gst_direct_control_binding_sync_values (GstControlBinding * - _self, GstObject * object, GstClockTime timestamp, GstClockTime last_sync); -static GValue *gst_direct_control_binding_get_value (GstControlBinding * _self, - GstClockTime timestamp); -static gboolean gst_direct_control_binding_get_value_array (GstControlBinding * - _self, GstClockTime timestamp, GstClockTime interval, guint n_values, - gpointer values); -static gboolean gst_direct_control_binding_get_g_value_array (GstControlBinding - * _self, GstClockTime timestamp, GstClockTime interval, guint n_values, - GValue * values); - -#define _do_init \ - GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gstdirectcontrolbinding", 0, \ - "dynamic parameter control source attachment"); - -#define gst_direct_control_binding_parent_class parent_class -G_DEFINE_TYPE_WITH_CODE (GstDirectControlBinding, gst_direct_control_binding, - GST_TYPE_CONTROL_BINDING, _do_init); - -enum -{ - PROP_0, - PROP_CS, - PROP_ABSOLUTE, - PROP_LAST -}; - -static GParamSpec *properties[PROP_LAST]; - -/* mapping functions */ - -#define DEFINE_CONVERT(type,Type,TYPE,ROUNDING_OP) \ -static void \ -convert_g_value_to_##type (GstDirectControlBinding *self, gdouble s, GValue *d) \ -{ \ - GParamSpec##Type *pspec = G_PARAM_SPEC_##TYPE (((GstControlBinding *)self)->pspec); \ - g##type v; \ - \ - s = CLAMP (s, 0.0, 1.0); \ - v = (g##type) ROUNDING_OP (pspec->minimum * (1-s)) + (g##type) ROUNDING_OP (pspec->maximum * s); \ - g_value_set_##type (d, v); \ -} \ -\ -static void \ -convert_value_to_##type (GstDirectControlBinding *self, gdouble s, gpointer d_) \ -{ \ - GParamSpec##Type *pspec = G_PARAM_SPEC_##TYPE (((GstControlBinding *)self)->pspec); \ - g##type *d = (g##type *)d_; \ - \ - s = CLAMP (s, 0.0, 1.0); \ - *d = (g##type) ROUNDING_OP (pspec->minimum * (1-s)) + (g##type) ROUNDING_OP (pspec->maximum * s); \ -} \ -\ -static void \ -abs_convert_g_value_to_##type (GstDirectControlBinding *self, gdouble s, GValue *d) \ -{ \ - g##type v; \ - v = (g##type) ROUNDING_OP (s); \ - g_value_set_##type (d, v); \ -} \ -\ -static void \ -abs_convert_value_to_##type (GstDirectControlBinding *self, gdouble s, gpointer d_) \ -{ \ - g##type *d = (g##type *)d_; \ - *d = (g##type) ROUNDING_OP (s); \ -} - -DEFINE_CONVERT (int, Int, INT, rint); -DEFINE_CONVERT (uint, UInt, UINT, rint); -DEFINE_CONVERT (long, Long, LONG, rint); -DEFINE_CONVERT (ulong, ULong, ULONG, rint); -DEFINE_CONVERT (int64, Int64, INT64, rint); -DEFINE_CONVERT (uint64, UInt64, UINT64, rint); -DEFINE_CONVERT (float, Float, FLOAT, /*NOOP*/); -DEFINE_CONVERT (double, Double, DOUBLE, /*NOOP*/); - -static void -convert_g_value_to_boolean (GstDirectControlBinding * self, gdouble s, - GValue * d) -{ - s = CLAMP (s, 0.0, 1.0); - g_value_set_boolean (d, (gboolean) (s + 0.5)); -} - -static void -convert_value_to_boolean (GstDirectControlBinding * self, gdouble s, - gpointer d_) -{ - gboolean *d = (gboolean *) d_; - - s = CLAMP (s, 0.0, 1.0); - *d = (gboolean) (s + 0.5); -} - -static void -convert_g_value_to_enum (GstDirectControlBinding * self, gdouble s, GValue * d) -{ - GParamSpecEnum *pspec = - G_PARAM_SPEC_ENUM (((GstControlBinding *) self)->pspec); - GEnumClass *e = pspec->enum_class; - gint v; - - s = CLAMP (s, 0.0, 1.0); - v = s * (e->n_values - 1); - g_value_set_enum (d, e->values[v].value); -} - -static void -convert_value_to_enum (GstDirectControlBinding * self, gdouble s, gpointer d_) -{ - GParamSpecEnum *pspec = - G_PARAM_SPEC_ENUM (((GstControlBinding *) self)->pspec); - GEnumClass *e = pspec->enum_class; - gint *d = (gint *) d_; - - s = CLAMP (s, 0.0, 1.0); - *d = e->values[(gint) (s * (e->n_values - 1))].value; -} - -/* vmethods */ - -static void -gst_direct_control_binding_class_init (GstDirectControlBindingClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GstControlBindingClass *control_binding_class = - GST_CONTROL_BINDING_CLASS (klass); - - gobject_class->constructor = gst_direct_control_binding_constructor; - gobject_class->set_property = gst_direct_control_binding_set_property; - gobject_class->get_property = gst_direct_control_binding_get_property; - gobject_class->dispose = gst_direct_control_binding_dispose; - gobject_class->finalize = gst_direct_control_binding_finalize; - - control_binding_class->sync_values = gst_direct_control_binding_sync_values; - control_binding_class->get_value = gst_direct_control_binding_get_value; - control_binding_class->get_value_array = - gst_direct_control_binding_get_value_array; - control_binding_class->get_g_value_array = - gst_direct_control_binding_get_g_value_array; - - properties[PROP_CS] = - g_param_spec_object ("control-source", "ControlSource", - "The control source", - GST_TYPE_CONTROL_SOURCE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); - - properties[PROP_ABSOLUTE] = - g_param_spec_boolean ("absolute", "Absolute", - "Whether the control values are absolute", - FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (gobject_class, PROP_LAST, properties); -} - -static void -gst_direct_control_binding_init (GstDirectControlBinding * self) -{ - self->last_value = G_MAXDOUBLE; -} - -static GObject * -gst_direct_control_binding_constructor (GType type, guint n_construct_params, - GObjectConstructParam * construct_params) -{ - GstDirectControlBinding *self; - - self = - GST_DIRECT_CONTROL_BINDING (G_OBJECT_CLASS (parent_class)->constructor - (type, n_construct_params, construct_params)); - - if (GST_CONTROL_BINDING_PSPEC (self)) { - GType type, base; - - base = type = G_PARAM_SPEC_VALUE_TYPE (GST_CONTROL_BINDING_PSPEC (self)); - g_value_init (&self->cur_value, type); - while ((type = g_type_parent (type))) - base = type; - - GST_DEBUG (" using type %s", g_type_name (base)); - - /* select mapping function */ - -#define SET_CONVERT_FUNCTION(type) \ - if (self->ABI.abi.want_absolute) { \ - self->convert_g_value = abs_convert_g_value_to_##type; \ - self->convert_value = abs_convert_value_to_##type; \ - } \ - else { \ - self->convert_g_value = convert_g_value_to_##type; \ - self->convert_value = convert_value_to_##type; \ - } \ - self->byte_size = sizeof (g##type); - - - switch (base) { - case G_TYPE_INT: - SET_CONVERT_FUNCTION (int); - break; - case G_TYPE_UINT: - SET_CONVERT_FUNCTION (uint); - break; - case G_TYPE_LONG: - SET_CONVERT_FUNCTION (long); - break; - case G_TYPE_ULONG: - SET_CONVERT_FUNCTION (ulong); - break; - case G_TYPE_INT64: - SET_CONVERT_FUNCTION (int64); - break; - case G_TYPE_UINT64: - SET_CONVERT_FUNCTION (uint64); - break; - case G_TYPE_FLOAT: - SET_CONVERT_FUNCTION (float); - break; - case G_TYPE_DOUBLE: - SET_CONVERT_FUNCTION (double); - break; - case G_TYPE_BOOLEAN: - self->convert_g_value = convert_g_value_to_boolean; - self->convert_value = convert_value_to_boolean; - self->byte_size = sizeof (gboolean); - break; - case G_TYPE_ENUM: - self->convert_g_value = convert_g_value_to_enum; - self->convert_value = convert_value_to_enum; - self->byte_size = sizeof (gint); - break; - default: - GST_WARNING ("incomplete implementation for paramspec type '%s'", - G_PARAM_SPEC_TYPE_NAME (GST_CONTROL_BINDING_PSPEC (self))); - GST_CONTROL_BINDING_PSPEC (self) = NULL; - break; - } - } - return (GObject *) self; -} - -static void -gst_direct_control_binding_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (object); - - switch (prop_id) { - case PROP_CS: - self->cs = g_value_dup_object (value); - break; - case PROP_ABSOLUTE: - self->ABI.abi.want_absolute = g_value_get_boolean (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_direct_control_binding_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (object); - - switch (prop_id) { - case PROP_CS: - g_value_set_object (value, self->cs); - break; - case PROP_ABSOLUTE: - g_value_set_boolean (value, self->ABI.abi.want_absolute); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_direct_control_binding_dispose (GObject * object) -{ - GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (object); - - if (self->cs) - gst_object_replace ((GstObject **) & self->cs, NULL); - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -gst_direct_control_binding_finalize (GObject * object) -{ - GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (object); - - if (G_IS_VALUE (&self->cur_value)) - g_value_unset (&self->cur_value); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static gboolean -gst_direct_control_binding_sync_values (GstControlBinding * _self, - GstObject * object, GstClockTime timestamp, GstClockTime last_sync) -{ - GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (_self); - gdouble src_val; - gboolean ret; - - g_return_val_if_fail (GST_IS_DIRECT_CONTROL_BINDING (self), FALSE); - g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE); - - GST_LOG_OBJECT (object, "property '%s' at ts=%" GST_TIME_FORMAT, - _self->name, GST_TIME_ARGS (timestamp)); - - ret = gst_control_source_get_value (self->cs, timestamp, &src_val); - if (G_LIKELY (ret)) { - GST_LOG_OBJECT (object, " new value %lf", src_val); - /* always set the value for first time, but then only if it changed - * this should limit g_object_notify invocations. - * FIXME: can we detect negative playback rates? - */ - if ((timestamp < last_sync) || (src_val != self->last_value)) { - GValue *dst_val = &self->cur_value; - - GST_LOG_OBJECT (object, " mapping %s to value of type %s", _self->name, - G_VALUE_TYPE_NAME (dst_val)); - /* run mapping function to convert gdouble to GValue */ - self->convert_g_value (self, src_val, dst_val); - /* we can make this faster - * http://bugzilla.gnome.org/show_bug.cgi?id=536939 - */ - g_object_set_property ((GObject *) object, _self->name, dst_val); - self->last_value = src_val; - } - } else { - GST_DEBUG_OBJECT (object, "no control value for param %s", _self->name); - } - return (ret); -} - -static GValue * -gst_direct_control_binding_get_value (GstControlBinding * _self, - GstClockTime timestamp) -{ - GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (_self); - GValue *dst_val = NULL; - gdouble src_val; - - g_return_val_if_fail (GST_IS_DIRECT_CONTROL_BINDING (self), NULL); - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), NULL); - g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE); - - /* get current value via control source */ - if (gst_control_source_get_value (self->cs, timestamp, &src_val)) { - dst_val = g_new0 (GValue, 1); - g_value_init (dst_val, G_PARAM_SPEC_VALUE_TYPE (_self->pspec)); - self->convert_g_value (self, src_val, dst_val); - } else { - GST_LOG ("no control value for property %s at ts %" GST_TIME_FORMAT, - _self->name, GST_TIME_ARGS (timestamp)); - } - - return dst_val; -} - -static gboolean -gst_direct_control_binding_get_value_array (GstControlBinding * _self, - GstClockTime timestamp, GstClockTime interval, guint n_values, - gpointer values_) -{ - GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (_self); - guint i; - gdouble *src_val; - gboolean res = FALSE; - GstDirectControlBindingConvertValue convert; - gint byte_size; - guint8 *values = (guint8 *) values_; - - g_return_val_if_fail (GST_IS_DIRECT_CONTROL_BINDING (self), FALSE); - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), FALSE); - g_return_val_if_fail (values, FALSE); - g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE); - - convert = self->convert_value; - byte_size = self->byte_size; - - src_val = g_new0 (gdouble, n_values); - if ((res = gst_control_source_get_value_array (self->cs, timestamp, - interval, n_values, src_val))) { - for (i = 0; i < n_values; i++) { - /* we will only get NAN for sparse control sources, such as triggers */ - if (!isnan (src_val[i])) { - convert (self, src_val[i], (gpointer) values); - } else { - GST_LOG ("no control value for property %s at index %d", _self->name, - i); - } - values += byte_size; - } - } else { - GST_LOG ("failed to get control value for property %s at ts %" - GST_TIME_FORMAT, _self->name, GST_TIME_ARGS (timestamp)); - } - g_free (src_val); - return res; -} - -static gboolean -gst_direct_control_binding_get_g_value_array (GstControlBinding * _self, - GstClockTime timestamp, GstClockTime interval, guint n_values, - GValue * values) -{ - GstDirectControlBinding *self = GST_DIRECT_CONTROL_BINDING (_self); - guint i; - gdouble *src_val; - gboolean res = FALSE; - GType type; - GstDirectControlBindingConvertGValue convert; - - g_return_val_if_fail (GST_IS_DIRECT_CONTROL_BINDING (self), FALSE); - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), FALSE); - g_return_val_if_fail (values, FALSE); - g_return_val_if_fail (GST_CONTROL_BINDING_PSPEC (self), FALSE); - - convert = self->convert_g_value; - type = G_PARAM_SPEC_VALUE_TYPE (_self->pspec); - - src_val = g_new0 (gdouble, n_values); - if ((res = gst_control_source_get_value_array (self->cs, timestamp, - interval, n_values, src_val))) { - for (i = 0; i < n_values; i++) { - /* we will only get NAN for sparse control sources, such as triggers */ - if (!isnan (src_val[i])) { - g_value_init (&values[i], type); - convert (self, src_val[i], &values[i]); - } else { - GST_LOG ("no control value for property %s at index %d", _self->name, - i); - } - } - } else { - GST_LOG ("failed to get control value for property %s at ts %" - GST_TIME_FORMAT, _self->name, GST_TIME_ARGS (timestamp)); - } - g_free (src_val); - return res; -} - -/* functions */ - -/** - * gst_direct_control_binding_new: - * @object: the object of the property - * @property_name: the property-name to attach the control source - * @cs: the control source - * - * Create a new control-binding that attaches the #GstControlSource to the - * #GObject property. It will map the control source range [0.0 ... 1.0] to - * the full target property range, and clip all values outside this range. - * - * Returns: (transfer floating): the new #GstDirectControlBinding - */ -GstControlBinding * -gst_direct_control_binding_new (GstObject * object, const gchar * property_name, - GstControlSource * cs) -{ - return (GstControlBinding *) g_object_new (GST_TYPE_DIRECT_CONTROL_BINDING, - "object", object, "name", property_name, "control-source", cs, NULL); -} - -/** - * gst_direct_control_binding_new_absolute: - * @object: the object of the property - * @property_name: the property-name to attach the control source - * @cs: the control source - * - * Create a new control-binding that attaches the #GstControlSource to the - * #GObject property. It will directly map the control source values to the - * target property range without any transformations. - * - * Returns: (transfer floating): the new #GstDirectControlBinding - * - * Since: 1.6 - */ -GstControlBinding * -gst_direct_control_binding_new_absolute (GstObject * object, - const gchar * property_name, GstControlSource * cs) -{ - return (GstControlBinding *) g_object_new (GST_TYPE_DIRECT_CONTROL_BINDING, - "object", object, "name", property_name, "control-source", cs, "absolute", - TRUE, NULL); -} diff --git a/libs/gst/controller/gstdirectcontrolbinding.h b/libs/gst/controller/gstdirectcontrolbinding.h deleted file mode 100644 index 549d1240ed..0000000000 --- a/libs/gst/controller/gstdirectcontrolbinding.h +++ /dev/null @@ -1,129 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2011 Stefan Sauer <ensonic@users.sf.net> - * - * gstdirectcontrolbinding.h: Direct attachment for control sources - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_DIRECT_CONTROL_BINDING_H__ -#define __GST_DIRECT_CONTROL_BINDING_H__ - -#include <gst/gstconfig.h> - -#include <glib-object.h> - -#include <gst/gstcontrolsource.h> -#include <gst/controller/controller-prelude.h> - -G_BEGIN_DECLS - -#define GST_TYPE_DIRECT_CONTROL_BINDING \ - (gst_direct_control_binding_get_type()) -#define GST_DIRECT_CONTROL_BINDING(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DIRECT_CONTROL_BINDING,GstDirectControlBinding)) -#define GST_DIRECT_CONTROL_BINDING_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DIRECT_CONTROL_BINDING,GstDirectControlBindingClass)) -#define GST_IS_DIRECT_CONTROL_BINDING(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DIRECT_CONTROL_BINDING)) -#define GST_IS_DIRECT_CONTROL_BINDING_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DIRECT_CONTROL_BINDING)) -#define GST_DIRECT_CONTROL_BINDING_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CONTOL_SOURCE, GstDirectControlBindingClass)) - -typedef struct _GstDirectControlBinding GstDirectControlBinding; -typedef struct _GstDirectControlBindingClass GstDirectControlBindingClass; - -/** - * GstDirectControlBindingConvertValue: - * @self: the #GstDirectControlBinding instance - * @src_value: the value returned by the cotnrol source - * @dest_value: the target location - * - * Function to map a control-value to the target plain data type. - */ -typedef void (* GstDirectControlBindingConvertValue) (GstDirectControlBinding *self, gdouble src_value, gpointer dest_value); - -/** - * GstDirectControlBindingConvertGValue: - * @self: the #GstDirectControlBinding instance - * @src_value: the value returned by the cotnrol source - * @dest_value: the target GValue - * - * Function to map a control-value to the target GValue. - */ -typedef void (* GstDirectControlBindingConvertGValue) (GstDirectControlBinding *self, gdouble src_value, GValue *dest_value); - -/** - * GstDirectControlBinding: - * @name: name of the property of this binding - * - * The instance structure of #GstDirectControlBinding. - */ -struct _GstDirectControlBinding { - GstControlBinding parent; - - /*< private >*/ - GstControlSource *cs; /* GstControlSource for this property */ - GValue cur_value; - gdouble last_value; - gint byte_size; - - GstDirectControlBindingConvertValue convert_value; - GstDirectControlBindingConvertGValue convert_g_value; - - union { - gpointer _gst_reserved[GST_PADDING]; - struct { - gboolean want_absolute; - } abi; - } ABI; -}; - -/** - * GstDirectControlBindingClass: - * @parent_class: Parent class - * @convert: Class method to convert control-values - * - * The class structure of #GstDirectControlBinding. - */ - -struct _GstDirectControlBindingClass -{ - GstControlBindingClass parent_class; - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; -}; - -GST_CONTROLLER_API -GType gst_direct_control_binding_get_type (void); - -/* Functions */ - -GST_CONTROLLER_API -GstControlBinding * gst_direct_control_binding_new (GstObject * object, const gchar * property_name, - GstControlSource * cs); -GST_CONTROLLER_API -GstControlBinding * gst_direct_control_binding_new_absolute (GstObject * object, const gchar * property_name, - GstControlSource * cs); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstDirectControlBinding, gst_object_unref) - -G_END_DECLS - -#endif /* __GST_DIRECT_CONTROL_BINDING_H__ */ diff --git a/libs/gst/controller/gstinterpolationcontrolsource.c b/libs/gst/controller/gstinterpolationcontrolsource.c deleted file mode 100644 index c0b6c86c55..0000000000 --- a/libs/gst/controller/gstinterpolationcontrolsource.c +++ /dev/null @@ -1,740 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007,2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * - * gstinterpolationcontrolsource.c: Control source that provides several - * interpolation methods - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * SECTION:gstinterpolationcontrolsource - * @title: GstInterpolationControlSource - * @short_description: interpolation control source - * - * #GstInterpolationControlSource is a #GstControlSource, that interpolates values between user-given - * control points. It supports several interpolation modes and property types. - * - * To use #GstInterpolationControlSource get a new instance by calling - * gst_interpolation_control_source_new(), bind it to a #GParamSpec and set some - * control points by calling gst_timed_value_control_source_set(). - * - * All functions are MT-safe. - * - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <glib-object.h> -#include <gst/gst.h> - -#include "gstinterpolationcontrolsource.h" -#include "gst/glib-compat-private.h" -#include "gst/math-compat.h" - -#define GST_CAT_DEFAULT controller_debug -GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); - -/* helper functions */ - -static inline gboolean -_get_nearest_control_points (GstTimedValueControlSource * self, - GstClockTime ts, GstControlPoint ** cp1, GstControlPoint ** cp2) -{ - GSequenceIter *iter; - - iter = gst_timed_value_control_source_find_control_point_iter (self, ts); - if (iter) { - *cp1 = g_sequence_get (iter); - iter = g_sequence_iter_next (iter); - if (iter && !g_sequence_iter_is_end (iter)) { - *cp2 = g_sequence_get (iter); - } else { - *cp2 = NULL; - } - return TRUE; - } - return FALSE; -} - -static inline void -_get_nearest_control_points2 (GstTimedValueControlSource * self, - GstClockTime ts, GstControlPoint ** cp1, GstControlPoint ** cp2, - GstClockTime * next_ts) -{ - GSequenceIter *iter1, *iter2 = NULL; - - *cp1 = *cp2 = NULL; - iter1 = gst_timed_value_control_source_find_control_point_iter (self, ts); - if (iter1) { - *cp1 = g_sequence_get (iter1); - iter2 = g_sequence_iter_next (iter1); - } else { - if (G_LIKELY (self->values)) { - /* all values in the control point list come after the given timestamp */ - iter2 = g_sequence_get_begin_iter (self->values); - /* why this? if !cp1 we don't interpolate anyway - * if we can eliminate this, we can also use _get_nearest_control_points() - * here, is this just to set next_ts? */ - } else { - /* no values */ - iter2 = NULL; - } - } - - if (iter2 && !g_sequence_iter_is_end (iter2)) { - *cp2 = g_sequence_get (iter2); - *next_ts = (*cp2)->timestamp; - } else { - *next_ts = GST_CLOCK_TIME_NONE; - } -} - - -/* steps-like (no-)interpolation, default */ -/* just returns the value for the most recent key-frame */ -static inline gdouble -_interpolate_none (GstTimedValueControlSource * self, GstControlPoint * cp) -{ - return cp->value; -} - -static gboolean -interpolate_none_get (GstTimedValueControlSource * self, GstClockTime timestamp, - gdouble * value) -{ - gboolean ret = FALSE; - GSequenceIter *iter; - GstControlPoint *cp; - - g_mutex_lock (&self->lock); - - iter = - gst_timed_value_control_source_find_control_point_iter (self, timestamp); - if (iter) { - cp = g_sequence_get (iter); - *value = _interpolate_none (self, cp); - ret = TRUE; - } - g_mutex_unlock (&self->lock); - return ret; -} - -static gboolean -interpolate_none_get_value_array (GstTimedValueControlSource * self, - GstClockTime timestamp, GstClockTime interval, guint n_values, - gdouble * values) -{ - gboolean ret = FALSE; - guint i; - GstClockTime ts = timestamp; - GstClockTime next_ts = 0; - GstControlPoint *cp1 = NULL, *cp2 = NULL; - - g_mutex_lock (&self->lock); - - for (i = 0; i < n_values; i++) { - GST_LOG ("values[%3d] : ts=%" GST_TIME_FORMAT ", next_ts=%" GST_TIME_FORMAT, - i, GST_TIME_ARGS (ts), GST_TIME_ARGS (next_ts)); - if (ts >= next_ts) { - _get_nearest_control_points2 (self, ts, &cp1, &cp2, &next_ts); - } - if (cp1) { - *values = _interpolate_none (self, cp1); - ret = TRUE; - GST_LOG ("values[%3d]=%lf", i, *values); - } else { - *values = NAN; - GST_LOG ("values[%3d]=-", i); - } - ts += interval; - values++; - } - g_mutex_unlock (&self->lock); - return ret; -} - - - -/* linear interpolation */ -/* smoothes in between values */ -static inline gdouble -_interpolate_linear (GstClockTime timestamp1, gdouble value1, - GstClockTime timestamp2, gdouble value2, GstClockTime timestamp) -{ - if (GST_CLOCK_TIME_IS_VALID (timestamp2)) { - gdouble slope; - - slope = - (value2 - value1) / gst_guint64_to_gdouble (timestamp2 - timestamp1); - return value1 + (gst_guint64_to_gdouble (timestamp - timestamp1) * slope); - } else { - return value1; - } -} - -static gboolean -interpolate_linear_get (GstTimedValueControlSource * self, - GstClockTime timestamp, gdouble * value) -{ - gboolean ret = FALSE; - GstControlPoint *cp1, *cp2; - - g_mutex_lock (&self->lock); - - if (_get_nearest_control_points (self, timestamp, &cp1, &cp2)) { - *value = _interpolate_linear (cp1->timestamp, cp1->value, - (cp2 ? cp2->timestamp : GST_CLOCK_TIME_NONE), - (cp2 ? cp2->value : 0.0), timestamp); - ret = TRUE; - } - g_mutex_unlock (&self->lock); - return ret; -} - -static gboolean -interpolate_linear_get_value_array (GstTimedValueControlSource * self, - GstClockTime timestamp, GstClockTime interval, guint n_values, - gdouble * values) -{ - gboolean ret = FALSE; - guint i; - GstClockTime ts = timestamp; - GstClockTime next_ts = 0; - GstControlPoint *cp1 = NULL, *cp2 = NULL; - - g_mutex_lock (&self->lock); - - for (i = 0; i < n_values; i++) { - GST_LOG ("values[%3d] : ts=%" GST_TIME_FORMAT ", next_ts=%" GST_TIME_FORMAT, - i, GST_TIME_ARGS (ts), GST_TIME_ARGS (next_ts)); - if (ts >= next_ts) { - _get_nearest_control_points2 (self, ts, &cp1, &cp2, &next_ts); - } - if (cp1) { - *values = _interpolate_linear (cp1->timestamp, cp1->value, - (cp2 ? cp2->timestamp : GST_CLOCK_TIME_NONE), - (cp2 ? cp2->value : 0.0), ts); - ret = TRUE; - GST_LOG ("values[%3d]=%lf", i, *values); - } else { - *values = NAN; - GST_LOG ("values[%3d]=-", i); - } - ts += interval; - values++; - } - g_mutex_unlock (&self->lock); - return ret; -} - - - -/* cubic interpolation */ - -/* The following functions implement a natural cubic spline interpolator. - * For details look at http://en.wikipedia.org/wiki/Spline_interpolation - * - * Instead of using a real matrix with n^2 elements for the linear system - * of equations we use three arrays o, p, q to hold the tridiagonal matrix - * as following to save memory: - * - * p[0] q[0] 0 0 0 - * o[1] p[1] q[1] 0 0 - * 0 o[2] p[2] q[2] . - * . . . . . - */ - -static void -_interpolate_cubic_update_cache (GstTimedValueControlSource * self) -{ - gint i, n = self->nvalues; - gdouble *o = g_new0 (gdouble, n); - gdouble *p = g_new0 (gdouble, n); - gdouble *q = g_new0 (gdouble, n); - - gdouble *h = g_new0 (gdouble, n); - gdouble *b = g_new0 (gdouble, n); - gdouble *z = g_new0 (gdouble, n); - - GSequenceIter *iter; - GstControlPoint *cp; - GstClockTime x, x_next; - gdouble y_prev, y, y_next; - - /* Fill linear system of equations */ - iter = g_sequence_get_begin_iter (self->values); - cp = g_sequence_get (iter); - x = cp->timestamp; - y = cp->value; - - p[0] = 1.0; - - iter = g_sequence_iter_next (iter); - cp = g_sequence_get (iter); - x_next = cp->timestamp; - y_next = cp->value; - h[0] = gst_guint64_to_gdouble (x_next - x); - - for (i = 1; i < n - 1; i++) { - /* Shuffle x and y values */ - y_prev = y; - x = x_next; - y = y_next; - iter = g_sequence_iter_next (iter); - cp = g_sequence_get (iter); - x_next = cp->timestamp; - y_next = cp->value; - - h[i] = gst_guint64_to_gdouble (x_next - x); - o[i] = h[i - 1]; - p[i] = 2.0 * (h[i - 1] + h[i]); - q[i] = h[i]; - b[i] = (y_next - y) / h[i] - (y - y_prev) / h[i - 1]; - } - p[n - 1] = 1.0; - - /* Use Gauss elimination to set everything below the diagonal to zero */ - for (i = 1; i < n - 1; i++) { - gdouble a = o[i] / p[i - 1]; - p[i] -= a * q[i - 1]; - b[i] -= a * b[i - 1]; - } - - /* Solve everything else from bottom to top */ - for (i = n - 2; i > 0; i--) - z[i] = (b[i] - q[i] * z[i + 1]) / p[i]; - - /* Save cache next in the GstControlPoint */ - - iter = g_sequence_get_begin_iter (self->values); - for (i = 0; i < n; i++) { - cp = g_sequence_get (iter); - cp->cache.cubic.h = h[i]; - cp->cache.cubic.z = z[i]; - iter = g_sequence_iter_next (iter); - } - - /* Free our temporary arrays */ - g_free (o); - g_free (p); - g_free (q); - g_free (h); - g_free (b); - g_free (z); -} - -static inline gdouble -_interpolate_cubic (GstTimedValueControlSource * self, GstControlPoint * cp1, - gdouble value1, GstControlPoint * cp2, gdouble value2, - GstClockTime timestamp) -{ - if (!self->valid_cache) { - _interpolate_cubic_update_cache (self); - self->valid_cache = TRUE; - } - - if (cp2) { - gdouble diff1, diff2; - gdouble out; - - diff1 = gst_guint64_to_gdouble (timestamp - cp1->timestamp); - diff2 = gst_guint64_to_gdouble (cp2->timestamp - timestamp); - - out = - (cp2->cache.cubic.z * diff1 * diff1 * diff1 + - cp1->cache.cubic.z * diff2 * diff2 * diff2) / cp1->cache.cubic.h; - out += - (value2 / cp1->cache.cubic.h - - cp1->cache.cubic.h * cp2->cache.cubic.z) * diff1; - out += - (value1 / cp1->cache.cubic.h - - cp1->cache.cubic.h * cp1->cache.cubic.z) * diff2; - return out; - } else { - return value1; - } -} - -static gboolean -interpolate_cubic_get (GstTimedValueControlSource * self, - GstClockTime timestamp, gdouble * value) -{ - gboolean ret = FALSE; - GstControlPoint *cp1, *cp2 = NULL; - - if (self->nvalues <= 2) - return interpolate_linear_get (self, timestamp, value); - - g_mutex_lock (&self->lock); - - if (_get_nearest_control_points (self, timestamp, &cp1, &cp2)) { - *value = _interpolate_cubic (self, cp1, cp1->value, cp2, - (cp2 ? cp2->value : 0.0), timestamp); - ret = TRUE; - } - g_mutex_unlock (&self->lock); - return ret; -} - -static gboolean -interpolate_cubic_get_value_array (GstTimedValueControlSource * self, - GstClockTime timestamp, GstClockTime interval, guint n_values, - gdouble * values) -{ - gboolean ret = FALSE; - guint i; - GstClockTime ts = timestamp; - GstClockTime next_ts = 0; - GstControlPoint *cp1 = NULL, *cp2 = NULL; - - if (self->nvalues <= 2) - return interpolate_linear_get_value_array (self, timestamp, interval, - n_values, values); - - g_mutex_lock (&self->lock); - - for (i = 0; i < n_values; i++) { - GST_LOG ("values[%3d] : ts=%" GST_TIME_FORMAT ", next_ts=%" GST_TIME_FORMAT, - i, GST_TIME_ARGS (ts), GST_TIME_ARGS (next_ts)); - if (ts >= next_ts) { - _get_nearest_control_points2 (self, ts, &cp1, &cp2, &next_ts); - } - if (cp1) { - *values = _interpolate_cubic (self, cp1, cp1->value, cp2, - (cp2 ? cp2->value : 0.0), ts); - ret = TRUE; - GST_LOG ("values[%3d]=%lf", i, *values); - } else { - *values = NAN; - GST_LOG ("values[%3d]=-", i); - } - ts += interval; - values++; - } - g_mutex_unlock (&self->lock); - return ret; -} - - -/* monotonic cubic interpolation */ - -/* the following functions implement monotonic cubic spline interpolation. - * For details: http://en.wikipedia.org/wiki/Monotone_cubic_interpolation - * - * In contrast to the previous cubic mode, the values won't overshoot. - */ - -static void -_interpolate_cubic_monotonic_update_cache (GstTimedValueControlSource * self) -{ - gint i, n = self->nvalues; - gdouble *dxs = g_new0 (gdouble, n); - gdouble *dys = g_new0 (gdouble, n); - gdouble *ms = g_new0 (gdouble, n); - gdouble *c1s = g_new0 (gdouble, n); - - GSequenceIter *iter; - GstControlPoint *cp; - GstClockTime x, x_next, dx; - gdouble y, y_next, dy; - - /* Get consecutive differences and slopes */ - iter = g_sequence_get_begin_iter (self->values); - cp = g_sequence_get (iter); - x_next = cp->timestamp; - y_next = cp->value; - for (i = 0; i < n - 1; i++) { - x = x_next; - y = y_next; - iter = g_sequence_iter_next (iter); - cp = g_sequence_get (iter); - x_next = cp->timestamp; - y_next = cp->value; - - dx = gst_guint64_to_gdouble (x_next - x); - dy = y_next - y; - dxs[i] = dx; - dys[i] = dy; - ms[i] = dy / dx; - } - - /* Get degree-1 coefficients */ - c1s[0] = ms[0]; - for (i = 1; i < n; i++) { - gdouble m = ms[i - 1]; - gdouble m_next = ms[i]; - - if (m * m_next <= 0) { - c1s[i] = 0.0; - } else { - gdouble dx_next, dx_sum; - - dx = dxs[i], dx_next = dxs[i + 1], dx_sum = dx + dx_next; - c1s[i] = 3.0 * dx_sum / ((dx_sum + dx_next) / m + (dx_sum + dx) / m_next); - } - } - c1s[n - 1] = ms[n - 1]; - - /* Get degree-2 and degree-3 coefficients */ - iter = g_sequence_get_begin_iter (self->values); - for (i = 0; i < n - 1; i++) { - gdouble c1, m, inv_dx, common; - cp = g_sequence_get (iter); - - c1 = c1s[i]; - m = ms[i]; - inv_dx = 1.0 / dxs[i]; - common = c1 + c1s[i + 1] - m - m; - - cp->cache.cubic_monotonic.c1s = c1; - cp->cache.cubic_monotonic.c2s = (m - c1 - common) * inv_dx; - cp->cache.cubic_monotonic.c3s = common * inv_dx * inv_dx; - - iter = g_sequence_iter_next (iter); - } - - /* Free our temporary arrays */ - g_free (dxs); - g_free (dys); - g_free (ms); - g_free (c1s); -} - -static inline gdouble -_interpolate_cubic_monotonic (GstTimedValueControlSource * self, - GstControlPoint * cp1, gdouble value1, GstControlPoint * cp2, - gdouble value2, GstClockTime timestamp) -{ - if (!self->valid_cache) { - _interpolate_cubic_monotonic_update_cache (self); - self->valid_cache = TRUE; - } - - if (cp2) { - gdouble diff = gst_guint64_to_gdouble (timestamp - cp1->timestamp); - gdouble diff2 = diff * diff; - gdouble out; - - out = value1 + cp1->cache.cubic_monotonic.c1s * diff; - out += cp1->cache.cubic_monotonic.c2s * diff2; - out += cp1->cache.cubic_monotonic.c3s * diff * diff2; - return out; - } else { - return value1; - } -} - -static gboolean -interpolate_cubic_monotonic_get (GstTimedValueControlSource * self, - GstClockTime timestamp, gdouble * value) -{ - gboolean ret = FALSE; - GstControlPoint *cp1, *cp2 = NULL; - - if (self->nvalues <= 2) - return interpolate_linear_get (self, timestamp, value); - - g_mutex_lock (&self->lock); - - if (_get_nearest_control_points (self, timestamp, &cp1, &cp2)) { - *value = _interpolate_cubic_monotonic (self, cp1, cp1->value, cp2, - (cp2 ? cp2->value : 0.0), timestamp); - ret = TRUE; - } - g_mutex_unlock (&self->lock); - return ret; -} - -static gboolean -interpolate_cubic_monotonic_get_value_array (GstTimedValueControlSource * self, - GstClockTime timestamp, GstClockTime interval, guint n_values, - gdouble * values) -{ - gboolean ret = FALSE; - guint i; - GstClockTime ts = timestamp; - GstClockTime next_ts = 0; - GstControlPoint *cp1 = NULL, *cp2 = NULL; - - if (self->nvalues <= 2) - return interpolate_linear_get_value_array (self, timestamp, interval, - n_values, values); - - g_mutex_lock (&self->lock); - - for (i = 0; i < n_values; i++) { - GST_LOG ("values[%3d] : ts=%" GST_TIME_FORMAT ", next_ts=%" GST_TIME_FORMAT, - i, GST_TIME_ARGS (ts), GST_TIME_ARGS (next_ts)); - if (ts >= next_ts) { - _get_nearest_control_points2 (self, ts, &cp1, &cp2, &next_ts); - } - if (cp1) { - *values = _interpolate_cubic_monotonic (self, cp1, cp1->value, cp2, - (cp2 ? cp2->value : 0.0), ts); - ret = TRUE; - GST_LOG ("values[%3d]=%lf", i, *values); - } else { - *values = NAN; - GST_LOG ("values[%3d]=-", i); - } - ts += interval; - values++; - } - g_mutex_unlock (&self->lock); - return ret; -} - - -static struct -{ - GstControlSourceGetValue get; - GstControlSourceGetValueArray get_value_array; -} interpolation_modes[] = { - { - (GstControlSourceGetValue) interpolate_none_get, - (GstControlSourceGetValueArray) interpolate_none_get_value_array}, { - (GstControlSourceGetValue) interpolate_linear_get, - (GstControlSourceGetValueArray) interpolate_linear_get_value_array}, { - (GstControlSourceGetValue) interpolate_cubic_get, - (GstControlSourceGetValueArray) interpolate_cubic_get_value_array}, { - (GstControlSourceGetValue) interpolate_cubic_monotonic_get, - (GstControlSourceGetValueArray) -interpolate_cubic_monotonic_get_value_array}}; - -static const guint num_interpolation_modes = G_N_ELEMENTS (interpolation_modes); - -enum -{ - PROP_MODE = 1 -}; - -struct _GstInterpolationControlSourcePrivate -{ - GstInterpolationMode interpolation_mode; -}; - -#define _do_init \ - GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "interpolation control source", 0, \ - "timeline value interpolating control source") - -G_DEFINE_TYPE_WITH_CODE (GstInterpolationControlSource, - gst_interpolation_control_source, GST_TYPE_TIMED_VALUE_CONTROL_SOURCE, - G_ADD_PRIVATE (GstInterpolationControlSource) - _do_init); - -/** - * gst_interpolation_control_source_new: - * - * This returns a new, unbound #GstInterpolationControlSource. - * - * Returns: (transfer full): a new, unbound #GstInterpolationControlSource. - */ -GstControlSource * -gst_interpolation_control_source_new (void) -{ - GstControlSource *csource = - g_object_new (GST_TYPE_INTERPOLATION_CONTROL_SOURCE, NULL); - - /* Clear floating flag */ - gst_object_ref_sink (csource); - - return csource; -} - -static gboolean - gst_interpolation_control_source_set_interpolation_mode - (GstInterpolationControlSource * self, GstInterpolationMode mode) -{ - GstControlSource *csource = GST_CONTROL_SOURCE (self); - - if (mode >= num_interpolation_modes || (int) mode < 0) { - GST_WARNING ("interpolation mode %d invalid or not implemented yet", mode); - return FALSE; - } - - GST_TIMED_VALUE_CONTROL_SOURCE_LOCK (self); - csource->get_value = interpolation_modes[mode].get; - csource->get_value_array = interpolation_modes[mode].get_value_array; - - gst_timed_value_control_invalidate_cache ((GstTimedValueControlSource *) - csource); - self->priv->interpolation_mode = mode; - - GST_TIMED_VALUE_CONTROL_SOURCE_UNLOCK (self); - - return TRUE; -} - -static void -gst_interpolation_control_source_init (GstInterpolationControlSource * self) -{ - self->priv = gst_interpolation_control_source_get_instance_private (self); - gst_interpolation_control_source_set_interpolation_mode (self, - GST_INTERPOLATION_MODE_NONE); -} - -static void -gst_interpolation_control_source_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstInterpolationControlSource *self = - GST_INTERPOLATION_CONTROL_SOURCE (object); - - switch (prop_id) { - case PROP_MODE: - gst_interpolation_control_source_set_interpolation_mode (self, - (GstInterpolationMode) g_value_get_enum (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_interpolation_control_source_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstInterpolationControlSource *self = - GST_INTERPOLATION_CONTROL_SOURCE (object); - - switch (prop_id) { - case PROP_MODE: - g_value_set_enum (value, self->priv->interpolation_mode); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_interpolation_control_source_class_init (GstInterpolationControlSourceClass - * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gobject_class->set_property = gst_interpolation_control_source_set_property; - gobject_class->get_property = gst_interpolation_control_source_get_property; - - g_object_class_install_property (gobject_class, PROP_MODE, - g_param_spec_enum ("mode", "Mode", "Interpolation mode", - GST_TYPE_INTERPOLATION_MODE, GST_INTERPOLATION_MODE_NONE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -} diff --git a/libs/gst/controller/gstinterpolationcontrolsource.h b/libs/gst/controller/gstinterpolationcontrolsource.h deleted file mode 100644 index a3da267fb5..0000000000 --- a/libs/gst/controller/gstinterpolationcontrolsource.h +++ /dev/null @@ -1,104 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org> - * - * gstinterpolationcontrolsource.h: Control source that provides several - * interpolation methods - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_INTERPOLATION_CONTROL_SOURCE_H__ -#define __GST_INTERPOLATION_CONTROL_SOURCE_H__ - -#include <glib-object.h> -#include <gst/gst.h> - -#include <gst/controller/gsttimedvaluecontrolsource.h> -#include <gst/controller/controller-enumtypes.h> - -G_BEGIN_DECLS - -#define GST_TYPE_INTERPOLATION_CONTROL_SOURCE \ - (gst_interpolation_control_source_get_type ()) -#define GST_INTERPOLATION_CONTROL_SOURCE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_INTERPOLATION_CONTROL_SOURCE, GstInterpolationControlSource)) -#define GST_INTERPOLATION_CONTROL_SOURCE_CLASS(vtable) \ - (G_TYPE_CHECK_CLASS_CAST ((vtable), GST_TYPE_INTERPOLATION_CONTROL_SOURCE, GstInterpolationControlSourceClass)) -#define GST_IS_INTERPOLATION_CONTROL_SOURCE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_INTERPOLATION_CONTROL_SOURCE)) -#define GST_IS_INTERPOLATION_CONTROL_SOURCE_CLASS(vtable) \ - (G_TYPE_CHECK_CLASS_TYPE ((vtable), GST_TYPE_INTERPOLATION_CONTROL_SOURCE)) -#define GST_INTERPOLATION_CONTROL_SOURCE_GET_CLASS(inst) \ - (G_TYPE_INSTANCE_GET_CLASS ((inst), GST_TYPE_INTERPOLATION_CONTROL_SOURCE, GstInterpolationControlSourceClass)) - -typedef struct _GstInterpolationControlSource GstInterpolationControlSource; -typedef struct _GstInterpolationControlSourceClass GstInterpolationControlSourceClass; -typedef struct _GstInterpolationControlSourcePrivate GstInterpolationControlSourcePrivate; - -/** - * GstInterpolationMode: - * @GST_INTERPOLATION_MODE_NONE: steps-like interpolation, default - * @GST_INTERPOLATION_MODE_LINEAR: linear interpolation - * @GST_INTERPOLATION_MODE_CUBIC: cubic interpolation (natural), may overshoot - * the min or max values set by the control point, but is more 'curvy' - * @GST_INTERPOLATION_MODE_CUBIC_MONOTONIC: monotonic cubic interpolation, will not - * produce any values outside of the min-max range set by the control points - * (Since: 1.8) - * - * The various interpolation modes available. - */ -typedef enum -{ - GST_INTERPOLATION_MODE_NONE, - GST_INTERPOLATION_MODE_LINEAR, - GST_INTERPOLATION_MODE_CUBIC, - GST_INTERPOLATION_MODE_CUBIC_MONOTONIC, -} GstInterpolationMode; - -/** - * GstInterpolationControlSource: - * - * The instance structure of #GstControlSource. - */ -struct _GstInterpolationControlSource { - GstTimedValueControlSource parent; - - /*< private >*/ - GstInterpolationControlSourcePrivate *priv; - gpointer _gst_reserved[GST_PADDING]; -}; - -struct _GstInterpolationControlSourceClass { - GstTimedValueControlSourceClass parent_class; - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; -}; - -GST_CONTROLLER_API -GType gst_interpolation_control_source_get_type (void); - -/* Functions */ - -GST_CONTROLLER_API -GstControlSource * gst_interpolation_control_source_new (void); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstInterpolationControlSource, gst_object_unref) - -G_END_DECLS - -#endif /* __GST_INTERPOLATION_CONTROL_SOURCE_H__ */ diff --git a/libs/gst/controller/gstlfocontrolsource.c b/libs/gst/controller/gstlfocontrolsource.c deleted file mode 100644 index 1706efddfc..0000000000 --- a/libs/gst/controller/gstlfocontrolsource.c +++ /dev/null @@ -1,594 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007,2010 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * - * gstlfocontrolsource.c: Control source that provides some periodic waveforms - * as control values. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * SECTION:gstlfocontrolsource - * @title: GstLFOControlSource - * @short_description: LFO control source - * - * #GstLFOControlSource is a #GstControlSource, that provides several periodic - * waveforms as control values. - * - * To use #GstLFOControlSource get a new instance by calling - * gst_lfo_control_source_new(), bind it to a #GParamSpec and set the relevant - * properties. - * - * All functions are MT-safe. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <float.h> - -#include <glib-object.h> -#include <gst/gst.h> -#include <gst/gstcontrolsource.h> - -#include "gstlfocontrolsource.h" - -#include "gst/glib-compat-private.h" - -#include <gst/math-compat.h> - -#define GST_CAT_DEFAULT controller_debug -GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); - -struct _GstLFOControlSourcePrivate -{ - GstLFOWaveform waveform; - gdouble frequency; - GstClockTime period; - GstClockTime timeshift; - gdouble amplitude; - gdouble offset; -}; - -/* FIXME: as % in C is not the modulo operator we need here for - * negative numbers implement our own. Are there better ways? */ -static inline GstClockTime -_calculate_pos (GstClockTime timestamp, GstClockTime timeshift, - GstClockTime period) -{ - while (timestamp < timeshift) - timestamp += period; - - timestamp -= timeshift; - - return timestamp % period; -} - -static inline gdouble -_sine_get (GstLFOControlSource * self, gdouble amp, gdouble off, - GstClockTime timeshift, GstClockTime period, gdouble frequency, - GstClockTime timestamp) -{ - gdouble pos = - gst_guint64_to_gdouble (_calculate_pos (timestamp, timeshift, period)); - gdouble ret; - - ret = sin (2.0 * M_PI * (frequency / GST_SECOND) * pos); - ret *= amp; - ret += off; - - return ret; -} - -static gboolean -waveform_sine_get (GstLFOControlSource * self, GstClockTime timestamp, - gdouble * value) -{ - GstLFOControlSourcePrivate *priv = self->priv; - - gst_object_sync_values (GST_OBJECT (self), timestamp); - g_mutex_lock (&self->lock); - *value = _sine_get (self, priv->amplitude, priv->offset, priv->timeshift, - priv->period, priv->frequency, timestamp); - g_mutex_unlock (&self->lock); - return TRUE; -} - -static gboolean -waveform_sine_get_value_array (GstLFOControlSource * self, - GstClockTime timestamp, GstClockTime interval, guint n_values, - gdouble * values) -{ - GstLFOControlSourcePrivate *priv = self->priv; - guint i; - GstClockTime ts = timestamp; - - for (i = 0; i < n_values; i++) { - gst_object_sync_values (GST_OBJECT (self), ts); - g_mutex_lock (&self->lock); - *values = _sine_get (self, priv->amplitude, priv->offset, priv->timeshift, - priv->period, priv->frequency, ts); - g_mutex_unlock (&self->lock); - ts += interval; - values++; - } - return TRUE; -} - - -static inline gdouble -_square_get (GstLFOControlSource * self, gdouble amp, gdouble off, - GstClockTime timeshift, GstClockTime period, gdouble frequency, - GstClockTime timestamp) -{ - GstClockTime pos = _calculate_pos (timestamp, timeshift, period); - gdouble ret; - - if (pos >= period / 2) - ret = amp; - else - ret = -amp; - ret += off; - - return ret; -} - -static gboolean -waveform_square_get (GstLFOControlSource * self, GstClockTime timestamp, - gdouble * value) -{ - GstLFOControlSourcePrivate *priv = self->priv; - - gst_object_sync_values (GST_OBJECT (self), timestamp); - g_mutex_lock (&self->lock); - *value = _square_get (self, priv->amplitude, priv->offset, priv->timeshift, - priv->period, priv->frequency, timestamp); - g_mutex_unlock (&self->lock); - return TRUE; -} - -static gboolean -waveform_square_get_value_array (GstLFOControlSource * self, - GstClockTime timestamp, GstClockTime interval, guint n_values, - gdouble * values) -{ - GstLFOControlSourcePrivate *priv = self->priv; - guint i; - GstClockTime ts = timestamp; - - for (i = 0; i < n_values; i++) { - gst_object_sync_values (GST_OBJECT (self), ts); - g_mutex_lock (&self->lock); - *values = _square_get (self, priv->amplitude, priv->offset, priv->timeshift, - priv->period, priv->frequency, ts); - g_mutex_unlock (&self->lock); - ts += interval; - values++; - } - return TRUE; -} - -static inline gdouble -_saw_get (GstLFOControlSource * self, gdouble amp, gdouble off, - GstClockTime timeshift, GstClockTime period, gdouble frequency, - GstClockTime timestamp) -{ - gdouble pos = - gst_guint64_to_gdouble (_calculate_pos (timestamp, timeshift, period)); - gdouble per = gst_guint64_to_gdouble (period); - gdouble ret; - - ret = -((pos - per / 2.0) * ((2.0 * amp) / per)); - ret += off; - - return ret; -} - -static gboolean -waveform_saw_get (GstLFOControlSource * self, GstClockTime timestamp, - gdouble * value) -{ - GstLFOControlSourcePrivate *priv = self->priv; - - gst_object_sync_values (GST_OBJECT (self), timestamp); - g_mutex_lock (&self->lock); - *value = _saw_get (self, priv->amplitude, priv->offset, priv->timeshift, - priv->period, priv->frequency, timestamp); - g_mutex_unlock (&self->lock); - return TRUE; -} - -static gboolean -waveform_saw_get_value_array (GstLFOControlSource * self, - GstClockTime timestamp, GstClockTime interval, guint n_values, - gdouble * values) -{ - GstLFOControlSourcePrivate *priv = self->priv; - guint i; - GstClockTime ts = timestamp; - - for (i = 0; i < n_values; i++) { - gst_object_sync_values (GST_OBJECT (self), ts); - g_mutex_lock (&self->lock); - *values = _saw_get (self, priv->amplitude, priv->offset, priv->timeshift, - priv->period, priv->frequency, ts); - g_mutex_unlock (&self->lock); - ts += interval; - values++; - } - return TRUE; -} - -static inline gdouble -_rsaw_get (GstLFOControlSource * self, gdouble amp, gdouble off, - GstClockTime timeshift, GstClockTime period, gdouble frequency, - GstClockTime timestamp) -{ - gdouble pos = - gst_guint64_to_gdouble (_calculate_pos (timestamp, timeshift, period)); - gdouble per = gst_guint64_to_gdouble (period); - gdouble ret; - - ret = (pos - per / 2.0) * ((2.0 * amp) / per); - ret += off; - - return ret; -} - -static gboolean -waveform_rsaw_get (GstLFOControlSource * self, GstClockTime timestamp, - gdouble * value) -{ - GstLFOControlSourcePrivate *priv = self->priv; - - gst_object_sync_values (GST_OBJECT (self), timestamp); - g_mutex_lock (&self->lock); - *value = _rsaw_get (self, priv->amplitude, priv->offset, priv->timeshift, - priv->period, priv->frequency, timestamp); - g_mutex_unlock (&self->lock); - return TRUE; -} - -static gboolean -waveform_rsaw_get_value_array (GstLFOControlSource * self, - GstClockTime timestamp, GstClockTime interval, guint n_values, - gdouble * values) -{ - GstLFOControlSourcePrivate *priv = self->priv; - guint i; - GstClockTime ts = timestamp; - - for (i = 0; i < n_values; i++) { - gst_object_sync_values (GST_OBJECT (self), ts); - g_mutex_lock (&self->lock); - *values = _rsaw_get (self, priv->amplitude, priv->offset, priv->timeshift, - priv->period, priv->frequency, ts); - g_mutex_unlock (&self->lock); - ts += interval; - values++; - } - return TRUE; -} - - -static inline gdouble -_triangle_get (GstLFOControlSource * self, gdouble amp, gdouble off, - GstClockTime timeshift, GstClockTime period, gdouble frequency, - GstClockTime timestamp) -{ - gdouble pos = - gst_guint64_to_gdouble (_calculate_pos (timestamp, timeshift, period)); - gdouble per = gst_guint64_to_gdouble (period); - gdouble ret; - - if (pos <= 0.25 * per) - /* 1st quarter */ - ret = pos * ((4.0 * amp) / per); - else if (pos <= 0.75 * per) - /* 2nd & 3rd quarter */ - ret = -(pos - per / 2.0) * ((4.0 * amp) / per); - else - /* 4th quarter */ - ret = -(per - pos) * ((4.0 * amp) / per); - - ret += off; - - return ret; -} - -static gboolean -waveform_triangle_get (GstLFOControlSource * self, GstClockTime timestamp, - gdouble * value) -{ - GstLFOControlSourcePrivate *priv = self->priv; - - gst_object_sync_values (GST_OBJECT (self), timestamp); - g_mutex_lock (&self->lock); - *value = _triangle_get (self, priv->amplitude, priv->offset, priv->timeshift, - priv->period, priv->frequency, timestamp); - g_mutex_unlock (&self->lock); - return TRUE; -} - -static gboolean -waveform_triangle_get_value_array (GstLFOControlSource * self, - GstClockTime timestamp, GstClockTime interval, guint n_values, - gdouble * values) -{ - GstLFOControlSourcePrivate *priv = self->priv; - guint i; - GstClockTime ts = timestamp; - - for (i = 0; i < n_values; i++) { - gst_object_sync_values (GST_OBJECT (self), ts); - g_mutex_lock (&self->lock); - *values = - _triangle_get (self, priv->amplitude, priv->offset, priv->timeshift, - priv->period, priv->frequency, ts); - g_mutex_unlock (&self->lock); - ts += interval; - values++; - } - return TRUE; -} - -static struct -{ - GstControlSourceGetValue get; - GstControlSourceGetValueArray get_value_array; -} waveforms[] = { - { - (GstControlSourceGetValue) waveform_sine_get, - (GstControlSourceGetValueArray) waveform_sine_get_value_array}, { - (GstControlSourceGetValue) waveform_square_get, - (GstControlSourceGetValueArray) waveform_square_get_value_array}, { - (GstControlSourceGetValue) waveform_saw_get, - (GstControlSourceGetValueArray) waveform_saw_get_value_array}, { - (GstControlSourceGetValue) waveform_rsaw_get, - (GstControlSourceGetValueArray) waveform_rsaw_get_value_array}, { - (GstControlSourceGetValue) waveform_triangle_get, - (GstControlSourceGetValueArray) waveform_triangle_get_value_array} -}; - -static const guint num_waveforms = G_N_ELEMENTS (waveforms); - -enum -{ - PROP_WAVEFORM = 1, - PROP_FREQUENCY, - PROP_TIMESHIFT, - PROP_AMPLITUDE, - PROP_OFFSET -}; - -#define _do_init \ - GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "lfo control source", 0, "low frequency oscillator control source") - -#define gst_lfo_control_source_parent_class parent_class -G_DEFINE_TYPE_WITH_CODE (GstLFOControlSource, gst_lfo_control_source, - GST_TYPE_CONTROL_SOURCE, G_ADD_PRIVATE (GstLFOControlSource) _do_init); - -static void -gst_lfo_control_source_reset (GstLFOControlSource * self) -{ - GstControlSource *csource = GST_CONTROL_SOURCE (self); - - csource->get_value = NULL; - csource->get_value_array = NULL; -} - -/** - * gst_lfo_control_source_new: - * - * This returns a new, unbound #GstLFOControlSource. - * - * Returns: (transfer full): a new, unbound #GstLFOControlSource. - */ -GstControlSource * -gst_lfo_control_source_new (void) -{ - GstControlSource *csource = g_object_new (GST_TYPE_LFO_CONTROL_SOURCE, NULL); - - /* Clear floating flag */ - gst_object_ref_sink (csource); - - return csource; -} - -static gboolean -gst_lfo_control_source_set_waveform (GstLFOControlSource * self, - GstLFOWaveform waveform) -{ - GstControlSource *csource = GST_CONTROL_SOURCE (self); - - if (waveform >= num_waveforms || (int) waveform < 0) { - GST_WARNING ("waveform %d invalid or not implemented yet", waveform); - return FALSE; - } - - csource->get_value = waveforms[waveform].get; - csource->get_value_array = waveforms[waveform].get_value_array; - - self->priv->waveform = waveform; - - return TRUE; -} - -static void -gst_lfo_control_source_init (GstLFOControlSource * self) -{ - self->priv = gst_lfo_control_source_get_instance_private (self); - self->priv->waveform = gst_lfo_control_source_set_waveform (self, - GST_LFO_WAVEFORM_SINE); - self->priv->frequency = 1.0; - self->priv->amplitude = 1.0; - self->priv->period = GST_SECOND / self->priv->frequency; - self->priv->timeshift = 0; - - g_mutex_init (&self->lock); -} - -static void -gst_lfo_control_source_finalize (GObject * obj) -{ - GstLFOControlSource *self = GST_LFO_CONTROL_SOURCE (obj); - - gst_lfo_control_source_reset (self); - g_mutex_clear (&self->lock); - - G_OBJECT_CLASS (parent_class)->finalize (obj); -} - -static void -gst_lfo_control_source_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstLFOControlSource *self = GST_LFO_CONTROL_SOURCE (object); - - switch (prop_id) { - case PROP_WAVEFORM: - g_mutex_lock (&self->lock); - gst_lfo_control_source_set_waveform (self, - (GstLFOWaveform) g_value_get_enum (value)); - g_mutex_unlock (&self->lock); - break; - case PROP_FREQUENCY:{ - gdouble frequency = g_value_get_double (value); - - g_return_if_fail (((GstClockTime) (GST_SECOND / frequency)) != 0); - - g_mutex_lock (&self->lock); - self->priv->frequency = frequency; - self->priv->period = GST_SECOND / frequency; - g_mutex_unlock (&self->lock); - break; - } - case PROP_TIMESHIFT: - g_mutex_lock (&self->lock); - self->priv->timeshift = g_value_get_uint64 (value); - g_mutex_unlock (&self->lock); - break; - case PROP_AMPLITUDE: - g_mutex_lock (&self->lock); - self->priv->amplitude = g_value_get_double (value); - g_mutex_unlock (&self->lock); - break; - case PROP_OFFSET: - g_mutex_lock (&self->lock); - self->priv->offset = g_value_get_double (value); - g_mutex_unlock (&self->lock); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_lfo_control_source_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstLFOControlSource *self = GST_LFO_CONTROL_SOURCE (object); - - switch (prop_id) { - case PROP_WAVEFORM: - g_value_set_enum (value, self->priv->waveform); - break; - case PROP_FREQUENCY: - g_value_set_double (value, self->priv->frequency); - break; - case PROP_TIMESHIFT: - g_value_set_uint64 (value, self->priv->timeshift); - break; - case PROP_AMPLITUDE: - g_value_set_double (value, self->priv->amplitude); - break; - case PROP_OFFSET: - g_value_set_double (value, self->priv->offset); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_lfo_control_source_class_init (GstLFOControlSourceClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gobject_class->finalize = gst_lfo_control_source_finalize; - gobject_class->set_property = gst_lfo_control_source_set_property; - gobject_class->get_property = gst_lfo_control_source_get_property; - - /** - * GstLFOControlSource:waveform: - * - * Specifies the waveform that should be used for this #GstLFOControlSource. - */ - g_object_class_install_property (gobject_class, PROP_WAVEFORM, - g_param_spec_enum ("waveform", "Waveform", "Waveform", - GST_TYPE_LFO_WAVEFORM, GST_LFO_WAVEFORM_SINE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - /** - * GstLFOControlSource:frequency: - * - * Specifies the frequency that should be used for the waveform - * of this #GstLFOControlSource. It should be large enough - * so that the period is longer than one nanosecond. - */ - g_object_class_install_property (gobject_class, PROP_FREQUENCY, - g_param_spec_double ("frequency", "Frequency", - "Frequency of the waveform", DBL_MIN, G_MAXDOUBLE, 1.0, - G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); - - /** - * GstLFOControlSource:timeshift: - * - * Specifies the timeshift to the right that should be used for the waveform - * of this #GstLFOControlSource in nanoseconds. - * - * To get a n nanosecond shift to the left use - * "(GST_SECOND / frequency) - n". - * - */ - g_object_class_install_property (gobject_class, PROP_TIMESHIFT, - g_param_spec_uint64 ("timeshift", "Timeshift", - "Timeshift of the waveform to the right", 0, G_MAXUINT64, 0, - G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); - - /** - * GstLFOControlSource:amplitude: - * - * Specifies the amplitude for the waveform of this #GstLFOControlSource. - */ - g_object_class_install_property (gobject_class, PROP_AMPLITUDE, - g_param_spec_double ("amplitude", "Amplitude", - "Amplitude of the waveform", 0.0, 1.0, 1.0, - G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); - - /** - * GstLFOControlSource:offset: - * - * Specifies the value offset for the waveform of this #GstLFOControlSource. - */ - g_object_class_install_property (gobject_class, PROP_OFFSET, - g_param_spec_double ("offset", "Offset", "Offset of the waveform", - 0.0, 1.0, 1.0, - G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); -} diff --git a/libs/gst/controller/gstlfocontrolsource.h b/libs/gst/controller/gstlfocontrolsource.h deleted file mode 100644 index cc09d492c0..0000000000 --- a/libs/gst/controller/gstlfocontrolsource.h +++ /dev/null @@ -1,102 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org> - * - * gstlfocontrolsource.h: Control source that provides some periodic waveforms - * as control values. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_LFO_CONTROL_SOURCE_H__ -#define __GST_LFO_CONTROL_SOURCE_H__ - -#include <glib-object.h> -#include <gst/gst.h> -#include <gst/controller/controller-enumtypes.h> - -G_BEGIN_DECLS - -#define GST_TYPE_LFO_CONTROL_SOURCE \ - (gst_lfo_control_source_get_type ()) -#define GST_LFO_CONTROL_SOURCE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_LFO_CONTROL_SOURCE, GstLFOControlSource)) -#define GST_LFO_CONTROL_SOURCE_CLASS(vtable) \ - (G_TYPE_CHECK_CLASS_CAST ((vtable), GST_TYPE_LFO_CONTROL_SOURCE, GstLFOControlSourceClass)) -#define GST_IS_LFO_CONTROL_SOURCE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_LFO_CONTROL_SOURCE)) -#define GST_IS_LFO_CONTROL_SOURCE_CLASS(vtable) \ - (G_TYPE_CHECK_CLASS_TYPE ((vtable), GST_TYPE_LFO_CONTROL_SOURCE)) -#define GST_LFO_CONTROL_SOURCE_GET_CLASS(inst) \ - (G_TYPE_INSTANCE_GET_CLASS ((inst), GST_TYPE_LFO_CONTROL_SOURCE, GstLFOControlSourceClass)) - -typedef struct _GstLFOControlSource GstLFOControlSource; -typedef struct _GstLFOControlSourceClass GstLFOControlSourceClass; -typedef struct _GstLFOControlSourcePrivate GstLFOControlSourcePrivate; - -/** - * GstLFOWaveform: - * @GST_LFO_WAVEFORM_SINE: sine waveform - * @GST_LFO_WAVEFORM_SQUARE: square waveform - * @GST_LFO_WAVEFORM_SAW: saw waveform - * @GST_LFO_WAVEFORM_REVERSE_SAW: reverse saw waveform - * @GST_LFO_WAVEFORM_TRIANGLE: triangle waveform - * - * The various waveform modes available. - */ -typedef enum -{ - GST_LFO_WAVEFORM_SINE, - GST_LFO_WAVEFORM_SQUARE, - GST_LFO_WAVEFORM_SAW, - GST_LFO_WAVEFORM_REVERSE_SAW, - GST_LFO_WAVEFORM_TRIANGLE -} GstLFOWaveform; - -/** - * GstLFOControlSource: - * - * The instance structure of #GstControlSource. - */ -struct _GstLFOControlSource { - GstControlSource parent; - - /* <private> */ - GstLFOControlSourcePrivate *priv; - GMutex lock; - gpointer _gst_reserved[GST_PADDING]; -}; - -struct _GstLFOControlSourceClass { - GstControlSourceClass parent_class; - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; -}; - -GST_CONTROLLER_API -GType gst_lfo_control_source_get_type (void); - -/* Functions */ - -GST_CONTROLLER_API -GstControlSource *gst_lfo_control_source_new (void); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstLFOControlSource, gst_object_unref) - -G_END_DECLS - -#endif /* __GST_LFO_CONTROL_SOURCE_H__ */ diff --git a/libs/gst/controller/gstproxycontrolbinding.c b/libs/gst/controller/gstproxycontrolbinding.c deleted file mode 100644 index ddff085ccc..0000000000 --- a/libs/gst/controller/gstproxycontrolbinding.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * GStreamer - * Copyright (C) 2016 Matthew Waters <matthew@centricular.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ -/** - * SECTION:gstproxycontrolbinding - * @title: GstProxyControlBinding - * @short_description: attachment for forwarding control sources - * @see_also: #GstControlBinding - * - * A #GstControlBinding that forwards requests to another #GstControlBinding - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstproxycontrolbinding.h" - -G_DEFINE_TYPE (GstProxyControlBinding, - gst_proxy_control_binding, GST_TYPE_CONTROL_BINDING); - -static void -gst_proxy_control_binding_init (GstProxyControlBinding * self) -{ - g_weak_ref_init (&self->ref_object, NULL); -} - -static void -gst_proxy_control_binding_finalize (GObject * object) -{ - GstProxyControlBinding *self = (GstProxyControlBinding *) object; - - g_weak_ref_clear (&self->ref_object); - g_free (self->property_name); - - G_OBJECT_CLASS (gst_proxy_control_binding_parent_class)->finalize (object); -} - -static gboolean -gst_proxy_control_binding_sync_values (GstControlBinding * binding, - GstObject * object, GstClockTime timestamp, GstClockTime last_sync) -{ - GstProxyControlBinding *self = (GstProxyControlBinding *) - binding; - gboolean ret = TRUE; - GstObject *ref_object; - - ref_object = g_weak_ref_get (&self->ref_object); - if (ref_object) { - GstControlBinding *ref_binding = - gst_object_get_control_binding (ref_object, self->property_name); - if (ref_binding) { - ret = gst_control_binding_sync_values (ref_binding, ref_object, - timestamp, last_sync); - gst_object_unref (ref_binding); - } - gst_object_unref (ref_object); - } - - return ret; -} - -static GValue * -gst_proxy_control_binding_get_value (GstControlBinding * binding, - GstClockTime timestamp) -{ - GstProxyControlBinding *self = (GstProxyControlBinding *) - binding; - GValue *ret = NULL; - GstObject *ref_object; - - ref_object = g_weak_ref_get (&self->ref_object); - if (ref_object) { - GstControlBinding *ref_binding = - gst_object_get_control_binding (ref_object, self->property_name); - if (ref_binding) { - ret = gst_control_binding_get_value (ref_binding, timestamp); - gst_object_unref (ref_binding); - } - gst_object_unref (ref_object); - } - - return ret; -} - -static gboolean -gst_proxy_control_binding_get_value_array (GstControlBinding * binding, - GstClockTime timestamp, GstClockTime interval, guint n_values, - gpointer values) -{ - GstProxyControlBinding *self = (GstProxyControlBinding *) - binding; - gboolean ret = FALSE; - GstObject *ref_object; - - ref_object = g_weak_ref_get (&self->ref_object); - if (ref_object) { - GstControlBinding *ref_binding = - gst_object_get_control_binding (ref_object, self->property_name); - if (ref_binding) { - ret = gst_control_binding_get_value_array (ref_binding, timestamp, - interval, n_values, values); - gst_object_unref (ref_binding); - } - gst_object_unref (ref_object); - } - - return ret; -} - -static gboolean -gst_proxy_control_binding_get_g_value_array (GstControlBinding * - binding, GstClockTime timestamp, GstClockTime interval, guint n_values, - GValue * values) -{ - GstProxyControlBinding *self = (GstProxyControlBinding *) binding; - gboolean ret = FALSE; - GstObject *ref_object; - - ref_object = g_weak_ref_get (&self->ref_object); - if (ref_object) { - GstControlBinding *ref_binding = - gst_object_get_control_binding (ref_object, self->property_name); - if (ref_binding) { - ret = gst_control_binding_get_g_value_array (ref_binding, timestamp, - interval, n_values, values); - gst_object_unref (ref_binding); - } - gst_object_unref (ref_object); - } - - return ret; -} - -static void -gst_proxy_control_binding_class_init (GstProxyControlBindingClass * klass) -{ - GstControlBindingClass *cb_class = GST_CONTROL_BINDING_CLASS (klass); - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - cb_class->sync_values = gst_proxy_control_binding_sync_values; - cb_class->get_value = gst_proxy_control_binding_get_value; - cb_class->get_value_array = gst_proxy_control_binding_get_value_array; - cb_class->get_g_value_array = gst_proxy_control_binding_get_g_value_array; - - gobject_class->finalize = gst_proxy_control_binding_finalize; -} - -/** - * gst_proxy_control_binding_new: - * @object: (transfer none): a #GstObject - * @property_name: the property name in @object to control - * @ref_object: (transfer none): a #GstObject to forward all - * #GstControlBinding requests to - * @ref_property_name: the property_name in @ref_object to control - * - * #GstProxyControlBinding forwards all access to data or `sync_values()` - * requests from @property_name on @object to the control binding at - * @ref_property_name on @ref_object. - * - * Returns: (transfer floating): a new #GstControlBinding that proxies the control interface between - * properties on different #GstObject's - * - * Since: 1.12 - */ -GstControlBinding * -gst_proxy_control_binding_new (GstObject * object, const gchar * property_name, - GstObject * ref_object, const gchar * ref_property_name) -{ - GstProxyControlBinding *cb; - - g_return_val_if_fail (GST_IS_OBJECT (object), NULL); - g_return_val_if_fail (property_name != NULL, NULL); - g_return_val_if_fail (GST_IS_OBJECT (ref_object), NULL); - g_return_val_if_fail (ref_property_name != NULL, NULL); - - cb = g_object_new (GST_TYPE_PROXY_CONTROL_BINDING, "object", object, - "name", property_name, NULL); - - g_weak_ref_set (&cb->ref_object, ref_object); - cb->property_name = g_strdup (ref_property_name); - - return (GstControlBinding *) cb; -} diff --git a/libs/gst/controller/gstproxycontrolbinding.h b/libs/gst/controller/gstproxycontrolbinding.h deleted file mode 100644 index 42e8fd9ff7..0000000000 --- a/libs/gst/controller/gstproxycontrolbinding.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * GStreamer - * Copyright (C) 2016 Matthew Waters <matthew@centricular.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_PROXY_CONTROL_BINDING_H__ -#define __GST_PROXY_CONTROL_BINDING_H__ - -#include <gst/gst.h> -#include <gst/controller/controller-prelude.h> - -G_BEGIN_DECLS - -#define GST_TYPE_PROXY_CONTROL_BINDING (gst_proxy_control_binding_get_type()) -#define GST_PROXY_CONTROL_BINDING(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PROXY_CONTROL_BINDING,GstProxyControlBinding)) -#define GST_PROXY_CONTROL_BINDING_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PROXY_CONTROL_BINDING,GstProxyControlBindingClass)) -#define GST_IS_PROXY_CONTROL_BINDING(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PROXY_CONTROL_BINDING)) -#define GST_IS_PROXY_CONTROL_BINDING_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PROXY_CONTROL_BINDING)) -#define GST_PROXY_CONTROL_BINDING_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CONTOL_SOURCE, GstProxyControlBindingClass)) - -typedef struct _GstProxyControlBinding GstProxyControlBinding; -typedef struct _GstProxyControlBindingClass GstProxyControlBindingClass; - -/** - * GstProxyControlBinding: - * - * Opaque #GstProxyControlBinding struct - */ -struct _GstProxyControlBinding -{ - /* <private> */ - GstControlBinding parent; - - GWeakRef ref_object; - gchar *property_name; - - gpointer _padding[GST_PADDING]; -}; - -/** - * GstProxyControlBindingClass: - * - * Opaque #GstProxyControlBindingClass struct - */ -struct _GstProxyControlBindingClass -{ - /* <private> */ - GstControlBindingClass parent_class; - - gpointer _padding[GST_PADDING]; -}; - -GST_CONTROLLER_API -GType gst_proxy_control_binding_get_type (void); - -GST_CONTROLLER_API -GstControlBinding * gst_proxy_control_binding_new (GstObject * object, - const gchar * property_name, - GstObject * ref_object, - const gchar * ref_property_name); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstProxyControlBinding, gst_object_unref) -G_END_DECLS - -#endif /* __GST_PROXY_CONTROL_BINDING_H__ */ diff --git a/libs/gst/controller/gsttimedvaluecontrolsource.c b/libs/gst/controller/gsttimedvaluecontrolsource.c deleted file mode 100644 index 1f9c599952..0000000000 --- a/libs/gst/controller/gsttimedvaluecontrolsource.c +++ /dev/null @@ -1,518 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007,2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * 2011 Stefan Sauer <ensonic@users.sf.net> - * - * gsttimedvaluecontrolsource.c: Base class for timeed value based control - * sources - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * SECTION:gsttimedvaluecontrolsource - * @title: GstTimedValueControlSource - * @short_description: timed value control source base class - * - * Base class for #GstControlSource that use time-stamped values. - * - * When overriding bind, chain up first to give this bind implementation a - * chance to setup things. - * - * All functions are MT-safe. - * - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <glib-object.h> -#include <gst/gst.h> - -#include "gstinterpolationcontrolsource.h" -#include "gst/glib-compat-private.h" - -#define GST_CAT_DEFAULT controller_debug -GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); - -#define _do_init \ - GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "timed value control source", 0, \ - "timed value control source base class") - -#define gst_timed_value_control_source_parent_class parent_class -G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstTimedValueControlSource, - gst_timed_value_control_source, GST_TYPE_CONTROL_SOURCE, _do_init); - - -enum -{ - VALUE_CHANGED_SIGNAL, - VALUE_ADDED_SIGNAL, - VALUE_REMOVED_SIGNAL, - LAST_SIGNAL -}; - -static guint gst_timed_value_control_source_signals[LAST_SIGNAL] = { 0 }; - -/** - * gst_control_point_free: - * @cp: the object to free - * - * Frees all data allocated by a #GstControlPoint instance. - */ -void -gst_control_point_free (GstControlPoint * cp) -{ - g_return_if_fail (cp); - - g_slice_free (GstControlPoint, cp); -} - -/** - * gst_control_point_copy: - * @cp: The control point to copy - * - * Copies a #GstControlPoint - * - * Returns: A copy of @cp - */ -GstControlPoint * -gst_control_point_copy (GstControlPoint * cp) -{ - return g_slice_dup (GstControlPoint, cp); -} - -GType -gst_control_point_get_type (void) -{ - static gsize type_id = 0; - - if (g_once_init_enter (&type_id)) { - GType tmp = - g_boxed_type_register_static (g_intern_static_string - ("GstControlPoint"), - (GBoxedCopyFunc) gst_control_point_copy, - (GBoxedFreeFunc) gst_control_point_free); - g_once_init_leave (&type_id, tmp); - } - - return type_id; -} - -static void -gst_timed_value_control_source_reset (GstTimedValueControlSource * self) -{ - GstControlSource *csource = (GstControlSource *) self; - - csource->get_value = NULL; - csource->get_value_array = NULL; - - if (self->values) { - g_sequence_free (self->values); - self->values = NULL; - } - - self->nvalues = 0; - self->valid_cache = FALSE; -} - -/* - * gst_control_point_compare: - * @p1: a pointer to a #GstControlPoint - * @p2: a pointer to a #GstControlPoint - * - * Compare function for g_list operations that operates on two #GstControlPoint - * parameters. - */ -static gint -gst_control_point_compare (gconstpointer p1, gconstpointer p2) -{ - GstClockTime ct1 = ((GstControlPoint *) p1)->timestamp; - GstClockTime ct2 = ((GstControlPoint *) p2)->timestamp; - - return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1)); -} - -/* - * gst_control_point_find: - * @p1: a pointer to a #GstControlPoint - * @p2: a pointer to a #GstClockTime - * @user_data: supplied user data - * - * Compare function for g_sequence operations that operates on a #GstControlPoint and - * a #GstClockTime. - */ -static gint -gst_control_point_find (gconstpointer p1, gconstpointer p2, gpointer user_data) -{ - GstClockTime ct1 = ((GstControlPoint *) p1)->timestamp; - GstClockTime ct2 = *(GstClockTime *) p2; - - return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1)); -} - -static GstControlPoint * -_make_new_cp (GstTimedValueControlSource * self, GstClockTime timestamp, - const gdouble value) -{ - GstControlPoint *cp; - - /* create a new GstControlPoint */ - cp = g_slice_new0 (GstControlPoint); - cp->timestamp = timestamp; - cp->value = value; - - return cp; -} - -static void -gst_timed_value_control_source_set_internal (GstTimedValueControlSource * - self, GstClockTime timestamp, const gdouble value) -{ - GstControlPoint *cp; - - g_mutex_lock (&self->lock); - - /* check if a control point for the timestamp already exists */ - if (G_LIKELY (self->values)) { - GSequenceIter *iter = g_sequence_lookup (self->values, ×tamp, - (GCompareDataFunc) gst_control_point_find, NULL); - - if (iter) { - GstControlPoint *cp = g_sequence_get (iter); - - /* update control point */ - cp->value = value; - g_mutex_unlock (&self->lock); - - g_signal_emit (self, - gst_timed_value_control_source_signals[VALUE_CHANGED_SIGNAL], 0, cp); - goto done; - } - } else { - self->values = g_sequence_new ((GDestroyNotify) gst_control_point_free); - GST_INFO ("create new timed value sequence"); - } - - /* sort new cp into the prop->values list */ - cp = _make_new_cp (self, timestamp, value); - g_sequence_insert_sorted (self->values, cp, - (GCompareDataFunc) gst_control_point_compare, NULL); - self->nvalues++; - g_mutex_unlock (&self->lock); - - g_signal_emit (self, - gst_timed_value_control_source_signals[VALUE_ADDED_SIGNAL], 0, cp); - -done: - self->valid_cache = FALSE; -} - -/** - * gst_timed_value_control_source_find_control_point_iter: - * @self: the control source to search in - * @timestamp: the search key - * - * Find last value before given timestamp in control point list. - * If all values in the control point list come after the given - * timestamp or no values exist, %NULL is returned. - * - * For use in control source implementations. - * - * Returns: (transfer none): the found #GSequenceIter or %NULL - */ -GSequenceIter *gst_timed_value_control_source_find_control_point_iter - (GstTimedValueControlSource * self, GstClockTime timestamp) -{ - GSequenceIter *iter; - - if (!self->values) - return NULL; - - iter = - g_sequence_search (self->values, ×tamp, - (GCompareDataFunc) gst_control_point_find, NULL); - - /* g_sequence_search() returns the iter where timestamp - * would be inserted, i.e. the iter > timestamp, so - * we need to get the previous one. And of course, if - * there is no previous one, we return NULL. */ - if (g_sequence_iter_is_begin (iter)) - return NULL; - - return g_sequence_iter_prev (iter); -} - - -/** - * gst_timed_value_control_source_set: - * @self: the #GstTimedValueControlSource object - * @timestamp: the time the control-change is scheduled for - * @value: the control-value - * - * Set the value of given controller-handled property at a certain time. - * - * Returns: FALSE if the values couldn't be set, TRUE otherwise. - */ -gboolean -gst_timed_value_control_source_set (GstTimedValueControlSource * self, - GstClockTime timestamp, const gdouble value) -{ - g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), FALSE); - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); - - gst_timed_value_control_source_set_internal (self, timestamp, value); - - return TRUE; -} - -/** - * gst_timed_value_control_source_set_from_list: - * @self: the #GstTimedValueControlSource object - * @timedvalues: (transfer none) (element-type GstTimedValue): a list - * with #GstTimedValue items - * - * Sets multiple timed values at once. - * - * Returns: FALSE if the values couldn't be set, TRUE otherwise. - */ -gboolean -gst_timed_value_control_source_set_from_list (GstTimedValueControlSource * - self, const GSList * timedvalues) -{ - const GSList *node; - GstTimedValue *tv; - gboolean res = FALSE; - - g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), FALSE); - - for (node = timedvalues; node; node = g_slist_next (node)) { - tv = node->data; - if (!GST_CLOCK_TIME_IS_VALID (tv->timestamp)) { - GST_WARNING ("GstTimedValued with invalid timestamp passed to %s", - GST_FUNCTION); - } else { - gst_timed_value_control_source_set_internal (self, tv->timestamp, - tv->value); - res = TRUE; - } - } - return res; -} - -/** - * gst_timed_value_control_source_unset: - * @self: the #GstTimedValueControlSource object - * @timestamp: the time the control-change should be removed from - * - * Used to remove the value of given controller-handled property at a certain - * time. - * - * Returns: FALSE if the value couldn't be unset (i.e. not found, TRUE otherwise. - */ -gboolean -gst_timed_value_control_source_unset (GstTimedValueControlSource * self, - GstClockTime timestamp) -{ - GSequenceIter *iter; - gboolean res = FALSE; - GstControlPoint *cp = NULL; - - g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), FALSE); - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); - - g_mutex_lock (&self->lock); - /* check if a control point for the timestamp exists */ - if (G_LIKELY (self->values) && (iter = - g_sequence_lookup (self->values, ×tamp, - (GCompareDataFunc) gst_control_point_find, NULL))) { - - /* Iter contains the iter right after timestamp, i.e. - * we need to get the previous one and check the timestamp - */ - cp = g_slice_dup (GstControlPoint, g_sequence_get (iter)); - g_sequence_remove (iter); - self->nvalues--; - self->valid_cache = FALSE; - res = TRUE; - } - g_mutex_unlock (&self->lock); - - if (cp) { - g_signal_emit (self, - gst_timed_value_control_source_signals[VALUE_REMOVED_SIGNAL], 0, cp); - g_slice_free (GstControlPoint, cp); - } - - return res; -} - -/** - * gst_timed_value_control_source_unset_all: - * @self: the #GstTimedValueControlSource object - * - * Used to remove all time-stamped values of given controller-handled property - * - */ -void -gst_timed_value_control_source_unset_all (GstTimedValueControlSource * self) -{ - g_return_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self)); - - g_mutex_lock (&self->lock); - /* free GstControlPoint structures */ - if (self->values) { - g_sequence_free (self->values); - self->values = NULL; - } - self->nvalues = 0; - self->valid_cache = FALSE; - - g_mutex_unlock (&self->lock); -} - -static void -_append_control_point (GstControlPoint * cp, GQueue * res) -{ - g_queue_push_tail (res, cp); -} - -/** - * gst_timed_value_control_source_get_all: - * @self: the #GstTimedValueControlSource to get the list from - * - * Returns a read-only copy of the list of #GstTimedValue for the given property. - * Free the list after done with it. - * - * Returns: (transfer container) (element-type GstTimedValue): a copy - * of the list, or %NULL if the property isn't handled by the controller - */ -GList * -gst_timed_value_control_source_get_all (GstTimedValueControlSource * self) -{ - GQueue res = G_QUEUE_INIT; - - g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), NULL); - - g_mutex_lock (&self->lock); - if (G_LIKELY (self->values)) - g_sequence_foreach (self->values, (GFunc) _append_control_point, &res); - g_mutex_unlock (&self->lock); - - return res.head; -} - -/** - * gst_timed_value_control_source_get_count: - * @self: the #GstTimedValueControlSource to get the number of values from - * - * Get the number of control points that are set. - * - * Returns: the number of control points that are set. - */ -gint -gst_timed_value_control_source_get_count (GstTimedValueControlSource * self) -{ - g_return_val_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self), 0); - return self->nvalues; -} - -/** - * gst_timed_value_control_invalidate_cache: - * @self: the #GstTimedValueControlSource - * - * Reset the controlled value cache. - */ -void -gst_timed_value_control_invalidate_cache (GstTimedValueControlSource * self) -{ - g_return_if_fail (GST_IS_TIMED_VALUE_CONTROL_SOURCE (self)); - self->valid_cache = FALSE; -} - -static void -gst_timed_value_control_source_init (GstTimedValueControlSource * self) -{ - g_mutex_init (&self->lock); -} - -static void -gst_timed_value_control_source_finalize (GObject * obj) -{ - GstTimedValueControlSource *self = GST_TIMED_VALUE_CONTROL_SOURCE (obj); - - g_mutex_lock (&self->lock); - gst_timed_value_control_source_reset (self); - g_mutex_unlock (&self->lock); - g_mutex_clear (&self->lock); - - G_OBJECT_CLASS (parent_class)->finalize (obj); -} - -static void -gst_timed_value_control_source_class_init (GstTimedValueControlSourceClass - * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - //GstControlSourceClass *csource_class = GST_CONTROL_SOURCE_CLASS (klass); - - /** - * GstTimedValueControlSource::value-changed - * @self: The #GstTimedValueControlSource on which a #GstTimedValue has changed - * @timed_value: The #GstTimedValue where the value changed - * - * Emitted right after the new value has been set on @timed_signals - * - * Since: 1.6 - */ - gst_timed_value_control_source_signals[VALUE_CHANGED_SIGNAL] = - g_signal_new ("value-changed", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_FIRST, 0, NULL, - NULL, NULL, G_TYPE_NONE, 1, gst_control_point_get_type ()); - - /** - * GstTimedValueControlSource::value-added - * @self: The #GstTimedValueControlSource into which a #GstTimedValue has been - * added - * @timed_value: The newly added #GstTimedValue - * - * Emitted right after the new value has been added to @self - * - * Since: 1.6 - */ - gst_timed_value_control_source_signals[VALUE_ADDED_SIGNAL] = - g_signal_new ("value-added", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_FIRST, 0, NULL, - NULL, NULL, G_TYPE_NONE, 1, gst_control_point_get_type ()); - - /** - * GstTimedValueControlSource::value-removed - * @self: The #GstTimedValueControlSource from which a #GstTimedValue has been - * removed - * @timed_value: The removed #GstTimedValue - * - * Emitted when @timed_value is removed from @self - * - * Since: 1.6 - */ - gst_timed_value_control_source_signals[VALUE_REMOVED_SIGNAL] = - g_signal_new ("value-removed", G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_FIRST, 0, NULL, - NULL, NULL, G_TYPE_NONE, 1, gst_control_point_get_type ()); - - - gobject_class->finalize = gst_timed_value_control_source_finalize; -} diff --git a/libs/gst/controller/gsttimedvaluecontrolsource.h b/libs/gst/controller/gsttimedvaluecontrolsource.h deleted file mode 100644 index 2baa369a23..0000000000 --- a/libs/gst/controller/gsttimedvaluecontrolsource.h +++ /dev/null @@ -1,160 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org> - * 2011 Stefan Sauer <ensonic@users.sf.net> - * - * gsttimedvaluecontrolsource.h: Base class for timeed value based control - * sources - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_TIMED_VALUE_CONTROL_SOURCE_H__ -#define __GST_TIMED_VALUE_CONTROL_SOURCE_H__ - -#include <glib-object.h> -#include <gst/gst.h> -#include <gst/controller/controller-prelude.h> - -G_BEGIN_DECLS - -#define GST_TYPE_TIMED_VALUE_CONTROL_SOURCE \ - (gst_timed_value_control_source_get_type ()) -#define GST_TIMED_VALUE_CONTROL_SOURCE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TIMED_VALUE_CONTROL_SOURCE, GstTimedValueControlSource)) -#define GST_TIMED_VALUE_CONTROL_SOURCE_CLASS(vtable) \ - (G_TYPE_CHECK_CLASS_CAST ((vtable), GST_TYPE_TIMED_VALUE_CONTROL_SOURCE, GstTimedValueControlSourceClass)) -#define GST_IS_TIMED_VALUE_CONTROL_SOURCE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TIMED_VALUE_CONTROL_SOURCE)) -#define GST_IS_TIMED_VALUE_CONTROL_SOURCE_CLASS(vtable) \ - (G_TYPE_CHECK_CLASS_TYPE ((vtable), GST_TYPE_TIMED_VALUE_CONTROL_SOURCE)) -#define GST_TIMED_VALUE_CONTROL_SOURCE_GET_CLASS(inst) \ - (G_TYPE_INSTANCE_GET_CLASS ((inst), GST_TYPE_TIMED_VALUE_CONTROL_SOURCE, GstTimedValueControlSourceClass)) - -typedef struct _GstTimedValueControlSource GstTimedValueControlSource; -typedef struct _GstTimedValueControlSourceClass GstTimedValueControlSourceClass; -typedef struct _GstTimedValueControlSourcePrivate GstTimedValueControlSourcePrivate; -typedef struct _GstControlPoint GstControlPoint; - -/** - * GstControlPoint: - * @timestamp: timestamp of the value change - * @value: the new value - * - * An internal structure for value+time and various temporary - * values used for interpolation. This "inherits" from - * GstTimedValue. - */ -struct _GstControlPoint -{ - /* fields from GstTimedValue. DO NOT CHANGE! */ - GstClockTime timestamp; - gdouble value; - - /*< private >*/ - - /* Caches for the interpolators */ - /* FIXME: we should not have this here already ... */ - union { - struct { /* 16 bytes */ - gdouble h; - gdouble z; - } cubic; - struct { /* 24 bytes */ - gdouble c1s, c2s, c3s; - } cubic_monotonic; - guint8 _gst_reserved[64]; - } cache; -}; - -GST_CONTROLLER_API -GType gst_control_point_get_type (void); - -/** - * GstTimedValueControlSource: - * - * The instance structure of #GstControlSource. - */ -struct _GstTimedValueControlSource { - GstControlSource parent; - - /*< protected >*/ - GMutex lock; - - GSequence *values; /* List of GstControlPoint */ - gint nvalues; /* Number of control points */ - gboolean valid_cache; - - /*< private >*/ - GstTimedValueControlSourcePrivate *priv; - gpointer _gst_reserved[GST_PADDING]; -}; - -struct _GstTimedValueControlSourceClass { - GstControlSourceClass parent_class; - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; -}; - -#define GST_TIMED_VALUE_CONTROL_SOURCE_LOCK(o) \ - g_mutex_lock(&((GstTimedValueControlSource *)o)->lock) -#define GST_TIMED_VALUE_CONTROL_SOURCE_UNLOCK(o) \ - g_mutex_unlock(&((GstTimedValueControlSource *)o)->lock) - -GST_CONTROLLER_API -GType gst_timed_value_control_source_get_type (void); - -/* Functions */ - -GST_CONTROLLER_API -GSequenceIter * gst_timed_value_control_source_find_control_point_iter ( - GstTimedValueControlSource * self, - GstClockTime timestamp); -GST_CONTROLLER_API -gboolean gst_timed_value_control_source_set (GstTimedValueControlSource * self, - GstClockTime timestamp, - const gdouble value); -GST_CONTROLLER_API -gboolean gst_timed_value_control_source_set_from_list (GstTimedValueControlSource * self, - const GSList * timedvalues); -GST_CONTROLLER_API -gboolean gst_timed_value_control_source_unset (GstTimedValueControlSource * self, - GstClockTime timestamp); - -GST_CONTROLLER_API -void gst_timed_value_control_source_unset_all (GstTimedValueControlSource *self); - -GST_CONTROLLER_API -GList * gst_timed_value_control_source_get_all (GstTimedValueControlSource * self); - -GST_CONTROLLER_API -gint gst_timed_value_control_source_get_count (GstTimedValueControlSource * self); - -GST_CONTROLLER_API -void gst_timed_value_control_invalidate_cache (GstTimedValueControlSource * self); - -GST_CONTROLLER_API -void gst_control_point_free (GstControlPoint * cp); - -GST_CONTROLLER_API -GstControlPoint * gst_control_point_copy (GstControlPoint * cp); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstTimedValueControlSource, gst_object_unref) - -G_END_DECLS - -#endif /* __GST_TIMED_VALUE_CONTROL_SOURCE_H__ */ diff --git a/libs/gst/controller/gsttriggercontrolsource.c b/libs/gst/controller/gsttriggercontrolsource.c deleted file mode 100644 index 0b68cf1d5a..0000000000 --- a/libs/gst/controller/gsttriggercontrolsource.c +++ /dev/null @@ -1,263 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007,2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> - * 2011 Stefan Sauer <ensonic@users.sf.net> - * - * gsttriggercontrolsource.c: Control source that provides some values at time- - * stamps - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - - /** - * SECTION:gsttriggercontrolsource - * @title: GstTriggerControlSource - * @short_description: trigger control source - * - * #GstTriggerControlSource is a #GstControlSource, that returns values from user-given - * control points. It allows for a tolerance on the time-stamps. - * - * To use #GstTriggerControlSource get a new instance by calling - * gst_trigger_control_source_new(), bind it to a #GParamSpec and set some - * control points by calling gst_timed_value_control_source_set(). - * - * All functions are MT-safe. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <glib-object.h> -#include <gst/gst.h> - -#include "gsttriggercontrolsource.h" -#include "gst/glib-compat-private.h" -#include "gst/math-compat.h" - -#define GST_CAT_DEFAULT controller_debug -GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); - -struct _GstTriggerControlSourcePrivate -{ - gint64 tolerance; -}; - -/* control point accessors */ - -/* returns the default value of the property, except for times with specific values */ -/* needed for one-shot events, such as notes and triggers */ - -static inline gdouble -_interpolate_trigger (GstTimedValueControlSource * self, GSequenceIter * iter, - GstClockTime timestamp) -{ - GstControlPoint *cp; - gint64 tolerance = ((GstTriggerControlSource *) self)->priv->tolerance; - gboolean found = FALSE; - - cp = g_sequence_get (iter); - if (GST_CLOCK_DIFF (cp->timestamp, timestamp) <= tolerance) { - found = TRUE; - } else { - if ((iter = g_sequence_iter_next (iter)) && !g_sequence_iter_is_end (iter)) { - cp = g_sequence_get (iter); - if (GST_CLOCK_DIFF (timestamp, cp->timestamp) <= tolerance) { - found = TRUE; - } - } - } - if (found) { - return cp->value; - } - return NAN; -} - -static gboolean -interpolate_trigger_get (GstTimedValueControlSource * self, - GstClockTime timestamp, gdouble * value) -{ - gboolean ret = FALSE; - GSequenceIter *iter; - - g_mutex_lock (&self->lock); - - iter = - gst_timed_value_control_source_find_control_point_iter (self, timestamp); - if (iter) { - *value = _interpolate_trigger (self, iter, timestamp); - if (!isnan (*value)) - ret = TRUE; - } - g_mutex_unlock (&self->lock); - return ret; -} - -static gboolean -interpolate_trigger_get_value_array (GstTimedValueControlSource * self, - GstClockTime timestamp, GstClockTime interval, guint n_values, - gdouble * values) -{ - gboolean ret = FALSE; - guint i; - GstClockTime ts = timestamp; - GstClockTime next_ts = 0; - gdouble val; - GSequenceIter *iter1 = NULL, *iter2 = NULL; - gboolean triggered = FALSE; - - g_mutex_lock (&self->lock); - for (i = 0; i < n_values; i++) { - val = NAN; - if (ts >= next_ts) { - iter1 = gst_timed_value_control_source_find_control_point_iter (self, ts); - if (!iter1) { - if (G_LIKELY (self->values)) - iter2 = g_sequence_get_begin_iter (self->values); - else - iter2 = NULL; - } else { - iter2 = g_sequence_iter_next (iter1); - } - - if (iter2 && !g_sequence_iter_is_end (iter2)) { - GstControlPoint *cp; - - cp = g_sequence_get (iter2); - next_ts = cp->timestamp; - } else { - next_ts = GST_CLOCK_TIME_NONE; - } - - if (iter1) { - val = _interpolate_trigger (self, iter1, ts); - if (!isnan (val)) - ret = TRUE; - } else { - g_mutex_unlock (&self->lock); - return FALSE; - } - triggered = TRUE; - } else if (triggered) { - if (iter1) { - val = _interpolate_trigger (self, iter1, ts); - if (!isnan (val)) - ret = TRUE; - } else { - g_mutex_unlock (&self->lock); - return FALSE; - } - triggered = FALSE; - } - *values = val; - ts += interval; - values++; - } - g_mutex_unlock (&self->lock); - return ret; -} - -enum -{ - PROP_TOLERANCE = 1, -}; - -#define _do_init \ - GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "trigger control source", 0, \ - "timeline value trigger control source") - -G_DEFINE_TYPE_WITH_CODE (GstTriggerControlSource, gst_trigger_control_source, - GST_TYPE_TIMED_VALUE_CONTROL_SOURCE, G_ADD_PRIVATE (GstTriggerControlSource) - _do_init); - -/** - * gst_trigger_control_source_new: - * - * This returns a new, unbound #GstTriggerControlSource. - * - * Returns: (transfer full): a new, unbound #GstTriggerControlSource. - */ -GstControlSource * -gst_trigger_control_source_new (void) -{ - GstControlSource *csource = - g_object_new (GST_TYPE_TRIGGER_CONTROL_SOURCE, NULL); - - /* Clear floating flag */ - gst_object_ref_sink (csource); - - return csource; -} - -static void -gst_trigger_control_source_init (GstTriggerControlSource * self) -{ - GstControlSource *csource = (GstControlSource *) self; - - self->priv = gst_trigger_control_source_get_instance_private (self); - - csource->get_value = (GstControlSourceGetValue) interpolate_trigger_get; - csource->get_value_array = (GstControlSourceGetValueArray) - interpolate_trigger_get_value_array; -} - -static void -gst_trigger_control_source_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstTriggerControlSource *self = GST_TRIGGER_CONTROL_SOURCE (object); - - switch (prop_id) { - case PROP_TOLERANCE: - GST_TIMED_VALUE_CONTROL_SOURCE_LOCK (self); - self->priv->tolerance = g_value_get_int64 (value); - GST_TIMED_VALUE_CONTROL_SOURCE_UNLOCK (self); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_trigger_control_source_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstTriggerControlSource *self = GST_TRIGGER_CONTROL_SOURCE (object); - - switch (prop_id) { - case PROP_TOLERANCE: - g_value_set_int64 (value, self->priv->tolerance); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_trigger_control_source_class_init (GstTriggerControlSourceClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gobject_class->set_property = gst_trigger_control_source_set_property; - gobject_class->get_property = gst_trigger_control_source_get_property; - - g_object_class_install_property (gobject_class, PROP_TOLERANCE, - g_param_spec_int64 ("tolerance", "Tolerance", - "Amount of ns a control time can be off to still trigger", - 0, G_MAXINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - -} diff --git a/libs/gst/controller/gsttriggercontrolsource.h b/libs/gst/controller/gsttriggercontrolsource.h deleted file mode 100644 index 431456b970..0000000000 --- a/libs/gst/controller/gsttriggercontrolsource.h +++ /dev/null @@ -1,87 +0,0 @@ -/* GStreamer - * - * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org> - * 2011 Stefan Sauer <ensonic@users.sf.net> - * - * gsttriggercontrolsource.h: Control source that provides some values at time- - * stamps - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - - -#ifndef __GST_TRIGGER_CONTROL_SOURCE_H__ -#define __GST_TRIGGER_CONTROL_SOURCE_H__ - -#include <glib-object.h> -#include <gst/gst.h> - -#include <gst/controller/gsttimedvaluecontrolsource.h> - -G_BEGIN_DECLS - -#define GST_TYPE_TRIGGER_CONTROL_SOURCE \ - (gst_trigger_control_source_get_type ()) -#define GST_TRIGGER_CONTROL_SOURCE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TRIGGER_CONTROL_SOURCE, GstTriggerControlSource)) -#define GST_TRIGGER_CONTROL_SOURCE_CLASS(vtable) \ - (G_TYPE_CHECK_CLASS_CAST ((vtable), GST_TYPE_TRIGGER_CONTROL_SOURCE, GstTriggerControlSourceClass)) -#define GST_IS_TRIGGER_CONTROL_SOURCE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TRIGGER_CONTROL_SOURCE)) -#define GST_IS_TRIGGER_CONTROL_SOURCE_CLASS(vtable) \ - (G_TYPE_CHECK_CLASS_TYPE ((vtable), GST_TYPE_TRIGGER_CONTROL_SOURCE)) -#define GST_TRIGGER_CONTROL_SOURCE_GET_CLASS(inst) \ - (G_TYPE_INSTANCE_GET_CLASS ((inst), GST_TYPE_TRIGGER_CONTROL_SOURCE, GstTriggerControlSourceClass)) - -#define GST_TYPE_TRIGGER_WAVEFORM (gst_trigger_waveform_get_type ()) - -typedef struct _GstTriggerControlSource GstTriggerControlSource; -typedef struct _GstTriggerControlSourceClass GstTriggerControlSourceClass; -typedef struct _GstTriggerControlSourcePrivate GstTriggerControlSourcePrivate; - -/** - * GstTriggerControlSource: - * - * The instance structure of #GstControlSource. - */ -struct _GstTriggerControlSource { - GstTimedValueControlSource parent; - - /*< private >*/ - GstTriggerControlSourcePrivate *priv; - gpointer _gst_reserved[GST_PADDING]; -}; - -struct _GstTriggerControlSourceClass { - GstTimedValueControlSourceClass parent_class; - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; -}; - -GST_CONTROLLER_API -GType gst_trigger_control_source_get_type (void); - -/* Functions */ - -GST_CONTROLLER_API -GstControlSource *gst_trigger_control_source_new (void); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstTriggerControlSource, gst_object_unref) - -G_END_DECLS - -#endif /* __GST_TRIGGER_CONTROL_SOURCE_H__ */ diff --git a/libs/gst/controller/meson.build b/libs/gst/controller/meson.build deleted file mode 100644 index 08857cadb6..0000000000 --- a/libs/gst/controller/meson.build +++ /dev/null @@ -1,82 +0,0 @@ -gst_controller_sources = [ - 'gstargbcontrolbinding.c', - 'gstdirectcontrolbinding.c', - 'gsttimedvaluecontrolsource.c', - 'gstinterpolationcontrolsource.c', - 'gstproxycontrolbinding.c', - 'gsttriggercontrolsource.c', - 'gstlfocontrolsource.c', -] - -controller_mkenum_headers = [ - 'gstinterpolationcontrolsource.h', - 'gstlfocontrolsource.h', -] - -gst_controller_headers = controller_mkenum_headers + [ - 'gstargbcontrolbinding.h', - 'gstdirectcontrolbinding.h', - 'gsttimedvaluecontrolsource.h', - 'gstinterpolationcontrolsource.h', - 'gstproxycontrolbinding.h', - 'gsttriggercontrolsource.h', - 'gstlfocontrolsource.h', - 'controller-prelude.h', - 'controller.h', -] -install_headers(gst_controller_headers, subdir : 'gstreamer-1.0/gst/controller/') - -controller_enums = gnome.mkenums_simple('controller-enumtypes', - sources : controller_mkenum_headers, - header_prefix : '#include <gst/controller/controller-prelude.h>', - body_prefix : '#include "config.h"', - decorator : 'GST_CONTROLLER_API', - install_header : true, - install_dir : join_paths(get_option('includedir'), 'gstreamer-1.0/gst/controller')) -gstcontroller_c = controller_enums[0] -gstcontroller_h = controller_enums[1] - -gst_controller_gen_sources = [gstcontroller_h] -gst_controller = library('gstcontroller-@0@'.format(apiversion), - gst_controller_sources, gstcontroller_h, gstcontroller_c, - c_args : gst_c_args + ['-DBUILDING_GST_CONTROLLER'], - install : true, - version : libversion, - soversion : soversion, - darwin_versions : osxversion, - include_directories : [configinc, libsinc], - dependencies : [gobject_dep, glib_dep, mathlib, gst_dep], -) - -pkgconfig.generate(gst_controller, - libraries : [libgst], - variables : pkgconfig_variables, - subdirs : pkgconfig_subdirs, - name : 'gstreamer-controller-1.0', - description : 'Dynamic parameter control for GStreamer elements', -) - -if build_gir - gst_gir_extra_args = gir_init_section + [ '--c-include=gst/controller/controller.h' ] - gst_controller_gir = gnome.generate_gir(gst_controller, - sources : gst_controller_sources + gst_controller_headers + [gstcontroller_h] + [gstcontroller_c], - namespace : 'GstController', - nsversion : apiversion, - identifier_prefix : 'Gst', - symbol_prefix : 'gst', - export_packages : 'gstreamer-controller-1.0', - dependencies : [gst_dep], - include_directories : [configinc, libsinc, privinc], - includes : ['GLib-2.0', 'GObject-2.0', 'GModule-2.0', 'Gst-1.0'], - install : true, - extra_args : gst_gir_extra_args, - ) - gst_controller_gen_sources += [gst_controller_gir] -endif - -gst_controller_dep = declare_dependency(link_with : gst_controller, - include_directories : [libsinc], - sources: gst_controller_gen_sources, - dependencies : [gst_dep]) - -meson.override_dependency('gstreamer-controller-1.0', gst_controller_dep) diff --git a/libs/gst/helpers/glib_gobject_helper.py b/libs/gst/helpers/glib_gobject_helper.py deleted file mode 100644 index 5b08c386ba..0000000000 --- a/libs/gst/helpers/glib_gobject_helper.py +++ /dev/null @@ -1,70 +0,0 @@ -## -## imported from glib: glib/glib_gdb.py -## -import gdb -import sys - -if sys.version_info[0] >= 3: - long = int - -# This is not quite right, as local vars may override symname -def read_global_var (symname): - return gdb.selected_frame().read_var(symname) - -def g_quark_to_string (quark): - if quark is None: - return None - quark = long(quark) - if quark == 0: - return None - try: - val = read_global_var ("quarks") - max_q = long(read_global_var ("quark_seq_id")) - except: - try: - val = read_global_var ("g_quarks") - max_q = long(read_global_var ("g_quark_seq_id")) - except: - return None; - if quark < max_q: - return val[quark].string() - return None - -## -## imported from glib: gobject/gobject_gdb.py -## - -def g_type_to_typenode (gtype): - def lookup_fundamental_type (typenode): - if typenode == 0: - return None - val = read_global_var ("static_fundamental_type_nodes") - if val is None: - return None - return val[typenode >> 2].address - - gtype = long(gtype) - typenode = gtype - gtype % 4 - if typenode > (255 << 2): - typenode = gdb.Value(typenode).cast (gdb.lookup_type("TypeNode").pointer()) - else: - typenode = lookup_fundamental_type (typenode) - return typenode - -def g_type_to_name (gtype): - typenode = g_type_to_typenode(gtype) - if typenode != None: - return g_quark_to_string (typenode["qname"]) - return None - -def g_type_name_from_instance (instance): - if long(instance) != 0: - try: - inst = instance.cast (gdb.lookup_type("GTypeInstance").pointer()) - klass = inst["g_class"] - gtype = klass["g_type"] - name = g_type_to_name (gtype) - return name - except RuntimeError: - pass - return None diff --git a/libs/gst/helpers/gst-completion-helper.c b/libs/gst/helpers/gst-completion-helper.c deleted file mode 100644 index 95821c8ab2..0000000000 --- a/libs/gst/helpers/gst-completion-helper.c +++ /dev/null @@ -1,271 +0,0 @@ -/* GStreamer - * Copyright (C) 2015 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com> - * - * gst-completion-helper.c: tool to let other tools enjoy fast and powerful - * gstreamer-aware completion - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include <gst/gst.h> -#include <glib.h> -#include <stdlib.h> -#include <string.h> - -static GList * -get_pad_templates_info (GstElement * element, GstElementFactory * factory, - GstPadDirection direction) -{ - const GList *pads; - GstStaticPadTemplate *padtemplate; - GList *caps_list = NULL; - - if (gst_element_factory_get_num_pad_templates (factory) == 0) { - g_print (" none\n"); - return NULL; - } - - pads = gst_element_factory_get_static_pad_templates (factory); - while (pads) { - padtemplate = (GstStaticPadTemplate *) (pads->data); - pads = g_list_next (pads); - - if (padtemplate->direction != direction) - continue; - - if (padtemplate->static_caps.string) { - caps_list = - g_list_append (caps_list, - gst_static_caps_get (&padtemplate->static_caps)); - } - - } - - return caps_list; -} - -static GList * -_get_pad_caps (const gchar * factory_name, GstPadDirection direction) -{ - GstElementFactory *factory = gst_element_factory_find (factory_name); - GstElement *element = gst_element_factory_make (factory_name, NULL); - - if (!element) - return NULL; - if (!factory) - return NULL; - factory = - GST_ELEMENT_FACTORY (gst_plugin_feature_load (GST_PLUGIN_FEATURE - (factory))); - if (!factory) - return NULL; - return get_pad_templates_info (element, factory, direction); -} - -static gboolean -_are_linkable (GstPluginFeature * feature, GList * caps_list) -{ - gboolean print = FALSE; - GstElementFactory *factory = GST_ELEMENT_FACTORY (feature); - - GList *tmp; - print = FALSE; - for (tmp = caps_list; tmp; tmp = tmp->next) { - if (gst_element_factory_can_sink_any_caps (factory, tmp->data)) { - print = TRUE; - break; - } - } - - return print; -} - -static gboolean -_belongs_to_klass (GstElementFactory * factory, const gchar * klass) -{ - const gchar *factory_klass; - - - factory_klass = - gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_KLASS); - if (strstr (factory_klass, klass)) - return TRUE; - return FALSE; -} - -static void -_list_features (const gchar * compatible_with, const gchar * klass, - GstCaps * sinkcaps) -{ - GList *plugins, *orig_plugins; - GList *caps_list = NULL; - - if (compatible_with) { - caps_list = _get_pad_caps (compatible_with, GST_PAD_SRC); - } - - orig_plugins = plugins = gst_registry_get_plugin_list (gst_registry_get ()); - while (plugins) { - GList *features, *orig_features; - GstPlugin *plugin; - - plugin = (GstPlugin *) (plugins->data); - plugins = g_list_next (plugins); - - if (GST_OBJECT_FLAG_IS_SET (plugin, GST_PLUGIN_FLAG_BLACKLISTED)) { - continue; - } - - orig_features = features = - gst_registry_get_feature_list_by_plugin (gst_registry_get (), - gst_plugin_get_name (plugin)); - while (features) { - GstPluginFeature *feature; - - if (G_UNLIKELY (features->data == NULL)) - goto next; - feature = GST_PLUGIN_FEATURE (features->data); - - if (GST_IS_ELEMENT_FACTORY (feature)) { - gboolean print = TRUE; - if (caps_list) - print = _are_linkable (feature, caps_list); - if (print && klass) - print = _belongs_to_klass (GST_ELEMENT_FACTORY (feature), klass); - if (print && sinkcaps) - print = - gst_element_factory_can_sink_any_caps (GST_ELEMENT_FACTORY - (feature), sinkcaps); - - if (print) - g_print ("%s ", gst_plugin_feature_get_name (feature)); - } - - next: - features = g_list_next (features); - } - - gst_plugin_feature_list_free (orig_features); - } - - g_list_free (caps_list); - g_print ("\n"); - gst_plugin_list_free (orig_plugins); -} - -static void -_print_element_properties_info (GstElement * element) -{ - GParamSpec **property_specs; - guint num_properties, i; - - property_specs = g_object_class_list_properties - (G_OBJECT_GET_CLASS (element), &num_properties); - - for (i = 0; i < num_properties; i++) { - GParamSpec *param = property_specs[i]; - - if (param->flags & G_PARAM_WRITABLE) { - g_print ("%s= ", g_param_spec_get_name (param)); - } - } - - g_free (property_specs); -} - -static void -_list_element_properties (const gchar * factory_name) -{ - GstElement *element = gst_element_factory_make (factory_name, NULL); - - _print_element_properties_info (element); -} - -int -main (int argc, char *argv[]) -{ - gboolean list_features = FALSE; - gchar *compatible_with = NULL; - gchar *element = NULL; - gchar *klass = NULL; - gchar *caps_str = NULL; - GstCaps *sinkcaps = NULL; - gint exit_code = EXIT_SUCCESS; - - GOptionEntry options[] = { - {"list-features", 'l', 0, G_OPTION_ARG_NONE, &list_features, - "list all the available features", NULL}, - {"compatible-with", '\0', 0, G_OPTION_ARG_STRING, &compatible_with, - "Only print the elements that could be queued after this feature name", - NULL}, - {"element-properties", '\0', 0, G_OPTION_ARG_STRING, &element, - "The element to list properties on", NULL}, - {"klass", '\0', 0, G_OPTION_ARG_STRING, &klass, - "Only print the elements belonging to that klass", NULL}, - {"sinkcaps", '\0', 0, G_OPTION_ARG_STRING, &caps_str, - "Only print the elements that can sink these caps", NULL}, - {NULL} - }; - - GOptionContext *ctx; - GError *err = NULL; - - ctx = g_option_context_new ("PIPELINE-DESCRIPTION"); - g_option_context_add_main_entries (ctx, options, NULL); - g_option_context_add_group (ctx, gst_init_get_option_group ()); - if (!g_option_context_parse (ctx, &argc, &argv, &err)) { - if (err) - g_printerr ("Error initializing: %s\n", GST_STR_NULL (err->message)); - else - g_printerr ("Error initializing: Unknown error!\n"); - g_clear_error (&err); - g_option_context_free (ctx); - exit (1); - } - g_option_context_free (ctx); - - if (caps_str) { - sinkcaps = gst_caps_from_string (caps_str); - if (!sinkcaps) { - exit_code = EXIT_FAILURE; - goto done; - } - } - - if (compatible_with || klass || sinkcaps) { - _list_features (compatible_with, klass, sinkcaps); - goto done; - } - - if (element) { - _list_element_properties (element); - goto done; - } - - if (list_features) { - _list_features (NULL, NULL, NULL); - goto done; - } - -done: - if (sinkcaps) - gst_caps_unref (sinkcaps); - exit (exit_code); -} diff --git a/libs/gst/helpers/gst-plugin-scanner.c b/libs/gst/helpers/gst-plugin-scanner.c deleted file mode 100644 index cbefb29e00..0000000000 --- a/libs/gst/helpers/gst-plugin-scanner.c +++ /dev/null @@ -1,71 +0,0 @@ -/* GStreamer - * Copyright (C) 2008 Jan Schmidt <jan.schmidt@sun.com> - * - * gst-plugin-scanner.c: tool to load plugins out of process for scanning - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - * Helper binary that does plugin-loading out of process and feeds results - * back to the parent over fds. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include <gst/gst_private.h> -#include <gst/gst.h> - -#include <string.h> - -int -main (int argc, char *argv[]) -{ - gboolean res; - char **my_argv; - int my_argc; - - /* We may or may not have an executable path */ - if (argc != 2 && argc != 3) - return 1; - - if (strcmp (argv[1], "-l")) - return 1; - - my_argc = 2; - my_argv = g_malloc (my_argc * sizeof (char *)); - my_argv[0] = argv[0]; - my_argv[1] = (char *) "--gst-disable-registry-update"; - -#ifndef GST_DISABLE_REGISTRY - _gst_disable_registry_cache = TRUE; -#endif - - if (argc == 3) - _gst_executable_path = g_strdup (argv[2]); - - res = gst_init_check (&my_argc, &my_argv, NULL); - - g_free (my_argv); - if (!res) - return 1; - - /* Create registry scanner listener and run */ - if (!_gst_plugin_loader_client_run ()) - return 1; - - return 0; -} diff --git a/libs/gst/helpers/gst-ptp-helper.c b/libs/gst/helpers/gst-ptp-helper.c deleted file mode 100644 index 91a2570fac..0000000000 --- a/libs/gst/helpers/gst-ptp-helper.c +++ /dev/null @@ -1,689 +0,0 @@ -/* GStreamer - * Copyright (C) 2015 Sebastian Dröge <sebastian@centricular.com> - * - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/* Helper process that runs setuid root or with appropriate privileges to - * listen on ports < 1024, do multicast operations and get MAC addresses of - * interfaces. Privileges are dropped after these operations are done. - * - * It listens on the PTP multicast group on port 319 and 320 and forwards - * everything received there to stdout, while forwarding everything received - * on stdout to those sockets. - * Additionally it provides the MAC address of a network interface via stdout - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <stdlib.h> -#include <unistd.h> -#include <sys/types.h> -#include <errno.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <net/if.h> -#include <netinet/in.h> -#include <string.h> - -#ifdef HAVE_GETIFADDRS_AF_LINK -#include <ifaddrs.h> -#include <net/if_dl.h> -#endif - -#ifdef HAVE_PTP_HELPER_SETUID -#include <grp.h> -#include <pwd.h> -#endif - -#ifdef HAVE_PTP_HELPER_CAPABILITIES -#include <sys/capability.h> -#endif - -#include <glib.h> -#include <gio/gio.h> - -#include <gst/gst.h> -#include <gst/net/gstptp_private.h> - -#define PTP_MULTICAST_GROUP "224.0.1.129" -#define PTP_EVENT_PORT 319 -#define PTP_GENERAL_PORT 320 - -static gchar **ifaces = NULL; -static gboolean verbose = FALSE; -static guint64 clock_id = (guint64) - 1; -static guint8 clock_id_array[8]; - -static GOptionEntry opt_entries[] = { - {"interface", 'i', 0, G_OPTION_ARG_STRING_ARRAY, &ifaces, - "Interface to listen on", NULL}, - {"clock-id", 'c', 0, G_OPTION_ARG_INT64, &clock_id, - "PTP clock id", NULL}, - {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, - "Be verbose", NULL}, - {NULL} -}; - -static GSocketAddress *event_saddr, *general_saddr; -static GSocket *socket_event, *socket_general; -static GIOChannel *stdin_channel, *stdout_channel; - -static gboolean -have_socket_data_cb (GSocket * socket, GIOCondition condition, - gpointer user_data) -{ - gchar buffer[8192]; - gssize read; - gsize written; - GError *err = NULL; - GIOStatus status; - StdIOHeader header = { 0, }; - - read = g_socket_receive (socket, buffer, sizeof (buffer), NULL, &err); - if (read == -1) - g_error ("Failed to read from socket: %s", err->message); - g_clear_error (&err); - - if (verbose) - g_message ("Received %" G_GSSIZE_FORMAT " bytes from %s socket", read, - (socket == socket_event ? "event" : "general")); - - header.size = read; - header.type = (socket == socket_event) ? TYPE_EVENT : TYPE_GENERAL; - - status = - g_io_channel_write_chars (stdout_channel, (gchar *) & header, - sizeof (header), &written, &err); - if (status == G_IO_STATUS_ERROR) { - g_error ("Failed to write to stdout: %s", err->message); - g_clear_error (&err); - } else if (status == G_IO_STATUS_EOF) { - g_message ("EOF on stdout"); - exit (0); - } else if (status != G_IO_STATUS_NORMAL) { - g_error ("Unexpected stdout write status: %d", status); - } else if (written != sizeof (header)) { - g_error ("Unexpected write size: %" G_GSIZE_FORMAT, written); - } - - status = - g_io_channel_write_chars (stdout_channel, buffer, read, &written, &err); - if (status == G_IO_STATUS_ERROR) { - g_error ("Failed to write to stdout: %s", err->message); - g_clear_error (&err); - } else if (status == G_IO_STATUS_EOF) { - g_message ("EOF on stdout"); - exit (0); - } else if (status != G_IO_STATUS_NORMAL) { - g_error ("Unexpected stdout write status: %d", status); - } else if (written != read) { - g_error ("Unexpected write size: %" G_GSIZE_FORMAT, written); - } - - return G_SOURCE_CONTINUE; -} - -static gboolean -have_stdin_data_cb (GIOChannel * channel, GIOCondition condition, - gpointer user_data) -{ - GIOStatus status; - StdIOHeader header = { 0, }; - gchar buffer[8192]; - GError *err = NULL; - gsize read; - gssize written; - - if ((condition & G_IO_STATUS_EOF)) { - g_message ("EOF on stdin"); - exit (0); - } - - status = - g_io_channel_read_chars (channel, (gchar *) & header, sizeof (header), - &read, &err); - if (status == G_IO_STATUS_ERROR) { - g_error ("Failed to read from stdin: %s", err->message); - g_clear_error (&err); - } else if (status == G_IO_STATUS_EOF) { - g_message ("EOF on stdin"); - exit (0); - } else if (status != G_IO_STATUS_NORMAL) { - g_error ("Unexpected stdin read status: %d", status); - } else if (read != sizeof (header)) { - g_error ("Unexpected read size: %" G_GSIZE_FORMAT, read); - } else if (header.size > 8192) { - g_error ("Unexpected size: %u", header.size); - } - - status = g_io_channel_read_chars (channel, buffer, header.size, &read, &err); - if (status == G_IO_STATUS_ERROR) { - g_error ("Failed to read from stdin: %s", err->message); - g_clear_error (&err); - } else if (status == G_IO_STATUS_EOF) { - g_message ("EOF on stdin"); - exit (0); - } else if (status != G_IO_STATUS_NORMAL) { - g_error ("Unexpected stdin read status: %d", status); - } else if (read != header.size) { - g_error ("Unexpected read size: %" G_GSIZE_FORMAT, read); - } - - switch (header.type) { - case TYPE_EVENT: - case TYPE_GENERAL: - written = - g_socket_send_to (header.type == - TYPE_EVENT ? socket_event : socket_general, - (header.type == TYPE_EVENT ? event_saddr : general_saddr), buffer, - header.size, NULL, &err); - if (written == -1) - g_error ("Failed to write to socket: %s", err->message); - else if (written != header.size) - g_error ("Unexpected write size: %" G_GSSIZE_FORMAT, written); - g_clear_error (&err); - if (verbose) - g_message ("Sent %" G_GSSIZE_FORMAT " bytes to %s socket", read, - (header.type == TYPE_EVENT ? "event" : "general")); - break; - default: - break; - } - - return G_SOURCE_CONTINUE; -} - -static void -setup_sockets (void) -{ - GInetAddress *bind_addr, *mcast_addr; - GSocketAddress *bind_saddr; - GSource *socket_event_source, *socket_general_source; - gchar **probed_ifaces = NULL; - GError *err = NULL; - - /* Create sockets */ - socket_event = - g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, - G_SOCKET_PROTOCOL_UDP, &err); - if (!socket_event) - g_error ("Couldn't create event socket: %s", err->message); - g_clear_error (&err); - g_socket_set_multicast_loopback (socket_event, FALSE); - - socket_general = - g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM, - G_SOCKET_PROTOCOL_UDP, &err); - if (!socket_general) - g_error ("Couldn't create general socket: %s", err->message); - g_clear_error (&err); - g_socket_set_multicast_loopback (socket_general, FALSE); - - /* Bind sockets */ - bind_addr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4); - bind_saddr = g_inet_socket_address_new (bind_addr, PTP_EVENT_PORT); - if (!g_socket_bind (socket_event, bind_saddr, TRUE, &err)) - g_error ("Couldn't bind event socket: %s", err->message); - g_object_unref (bind_saddr); - bind_saddr = g_inet_socket_address_new (bind_addr, PTP_GENERAL_PORT); - if (!g_socket_bind (socket_general, bind_saddr, TRUE, &err)) - g_error ("Couldn't bind general socket: %s", err->message); - g_object_unref (bind_saddr); - g_object_unref (bind_addr); - - /* Probe all non-loopback interfaces */ - if (!ifaces) { -#if defined(HAVE_SIOCGIFCONF_SIOCGIFFLAGS_SIOCGIFHWADDR) - struct ifreq ifr; - struct ifconf ifc; - gchar buf[8192]; - - ifc.ifc_len = sizeof (buf); - ifc.ifc_buf = buf; - if (ioctl (g_socket_get_fd (socket_event), SIOCGIFCONF, &ifc) != -1) { - guint i, idx = 0; - - probed_ifaces = g_new0 (gchar *, ifc.ifc_len + 1); - - for (i = 0; i < ifc.ifc_len / sizeof (struct ifreq); i++) { - strncpy (ifr.ifr_name, ifc.ifc_req[i].ifr_name, IFNAMSIZ); - if (ioctl (g_socket_get_fd (socket_event), SIOCGIFFLAGS, &ifr) == 0) { - if ((ifr.ifr_flags & IFF_LOOPBACK)) - continue; - probed_ifaces[idx] = g_strndup (ifc.ifc_req[i].ifr_name, IFNAMSIZ); - idx++; - } else { - g_warning ("can't get flags of interface '%s'", - ifc.ifc_req[i].ifr_name); - probed_ifaces[idx] = g_strndup (ifc.ifc_req[i].ifr_name, IFNAMSIZ); - idx++; - } - if (idx != 0) - ifaces = probed_ifaces; - } - } -#elif defined(HAVE_GETIFADDRS_AF_LINK) - struct ifaddrs *ifaddr, *ifa; - - if (getifaddrs (&ifaddr) != -1) { - GPtrArray *arr; - - arr = g_ptr_array_new (); - - for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { - if ((ifa->ifa_flags & IFF_LOOPBACK)) - continue; - - if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_LINK) - continue; - - g_ptr_array_add (arr, g_strdup (ifa->ifa_name)); - } - freeifaddrs (ifaddr); - - g_ptr_array_add (arr, NULL); - ifaces = probed_ifaces = (gchar **) g_ptr_array_free (arr, FALSE); - } -#else -#warning "Implement something to list all network interfaces" -#endif - } - - /* Get a clock id from the MAC address if none was given */ - if (clock_id == (guint64) - 1) { - gboolean success = FALSE; - -#if defined(HAVE_SIOCGIFCONF_SIOCGIFFLAGS_SIOCGIFHWADDR) - struct ifreq ifr; - - if (ifaces) { - gchar **ptr = ifaces; - - while (*ptr) { - memcpy (ifr.ifr_name, *ptr, IFNAMSIZ); - if (ioctl (g_socket_get_fd (socket_event), SIOCGIFHWADDR, &ifr) == 0) { - clock_id_array[0] = ifr.ifr_hwaddr.sa_data[0]; - clock_id_array[1] = ifr.ifr_hwaddr.sa_data[1]; - clock_id_array[2] = ifr.ifr_hwaddr.sa_data[2]; - clock_id_array[3] = 0xff; - clock_id_array[4] = 0xfe; - clock_id_array[5] = ifr.ifr_hwaddr.sa_data[3]; - clock_id_array[6] = ifr.ifr_hwaddr.sa_data[4]; - clock_id_array[7] = ifr.ifr_hwaddr.sa_data[5]; - success = TRUE; - break; - } - } - - ptr++; - } else { - struct ifconf ifc; - gchar buf[8192]; - - ifc.ifc_len = sizeof (buf); - ifc.ifc_buf = buf; - if (ioctl (g_socket_get_fd (socket_event), SIOCGIFCONF, &ifc) != -1) { - guint i; - - for (i = 0; i < ifc.ifc_len / sizeof (struct ifreq); i++) { - strncpy (ifr.ifr_name, ifc.ifc_req[i].ifr_name, IFNAMSIZ); - if (ioctl (g_socket_get_fd (socket_event), SIOCGIFFLAGS, &ifr) == 0) { - if ((ifr.ifr_flags & IFF_LOOPBACK)) - continue; - - if (ioctl (g_socket_get_fd (socket_event), SIOCGIFHWADDR, - &ifr) == 0) { - clock_id_array[0] = ifr.ifr_hwaddr.sa_data[0]; - clock_id_array[1] = ifr.ifr_hwaddr.sa_data[1]; - clock_id_array[2] = ifr.ifr_hwaddr.sa_data[2]; - clock_id_array[3] = 0xff; - clock_id_array[4] = 0xfe; - clock_id_array[5] = ifr.ifr_hwaddr.sa_data[3]; - clock_id_array[6] = ifr.ifr_hwaddr.sa_data[4]; - clock_id_array[7] = ifr.ifr_hwaddr.sa_data[5]; - success = TRUE; - break; - } - } else { - g_warning ("can't get flags of interface '%s'", - ifc.ifc_req[i].ifr_name); - } - } - } - } -#elif defined(HAVE_GETIFADDRS_AF_LINK) - struct ifaddrs *ifaddr, *ifa; - - if (getifaddrs (&ifaddr) != -1) { - for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { - struct sockaddr_dl *sdl = (struct sockaddr_dl *) ifa->ifa_addr; - guint8 mac_addr[6]; - - if ((ifa->ifa_flags & IFF_LOOPBACK)) - continue; - - if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_LINK) - continue; - - if (ifaces) { - gchar **p = ifaces; - gboolean found = FALSE; - - while (*p) { - if (strcmp (*p, ifa->ifa_name) == 0) { - found = TRUE; - break; - } - p++; - } - - if (!found) - continue; - } - - if (sdl->sdl_alen != 6) - continue; - - memcpy (mac_addr, LLADDR (sdl), sdl->sdl_alen); - - clock_id_array[0] = mac_addr[0]; - clock_id_array[1] = mac_addr[1]; - clock_id_array[2] = mac_addr[2]; - clock_id_array[3] = 0xff; - clock_id_array[4] = 0xfe; - clock_id_array[5] = mac_addr[3]; - clock_id_array[6] = mac_addr[4]; - clock_id_array[7] = mac_addr[5]; - success = TRUE; - break; - } - - freeifaddrs (ifaddr); - } -#else -#warning "Implement something to get MAC addresses of network interfaces" -#endif - - if (!success) { - g_warning ("can't get any MAC address, using random clock id"); - clock_id = (((guint64) g_random_int ()) << 32) | (g_random_int ()); - GST_WRITE_UINT64_BE (clock_id_array, clock_id); - clock_id_array[3] = 0xff; - clock_id_array[4] = 0xfe; - } - } else { - GST_WRITE_UINT64_BE (clock_id_array, clock_id); - } - - /* Join multicast groups */ - mcast_addr = g_inet_address_new_from_string (PTP_MULTICAST_GROUP); - if (ifaces) { - gchar **ptr = ifaces; - gboolean success = FALSE; - - while (*ptr) { - gint c = 0; - if (!g_socket_join_multicast_group (socket_event, mcast_addr, FALSE, *ptr, - &err) - && !g_error_matches (err, G_IO_ERROR, G_IO_ERROR_ADDRESS_IN_USE)) - g_warning ("Couldn't join multicast group on interface '%s': %s", *ptr, - err->message); - else - c++; - g_clear_error (&err); - - if (!g_socket_join_multicast_group (socket_general, mcast_addr, FALSE, - *ptr, &err) - && !g_error_matches (err, G_IO_ERROR, G_IO_ERROR_ADDRESS_IN_USE)) - g_warning ("Couldn't join multicast group on interface '%s': %s", *ptr, - err->message); - else - c++; - g_clear_error (&err); - - if (c == 2) - success = TRUE; - ptr++; - } - - if (!success) { - /* Join multicast group without any interface */ - if (!g_socket_join_multicast_group (socket_event, mcast_addr, FALSE, NULL, - &err)) - g_error ("Couldn't join multicast group: %s", err->message); - g_clear_error (&err); - if (!g_socket_join_multicast_group (socket_general, mcast_addr, FALSE, - NULL, &err)) - g_error ("Couldn't join multicast group: %s", err->message); - g_clear_error (&err); - } - } else { - /* Join multicast group without any interface */ - if (!g_socket_join_multicast_group (socket_event, mcast_addr, FALSE, NULL, - &err)) - g_error ("Couldn't join multicast group: %s", err->message); - g_clear_error (&err); - if (!g_socket_join_multicast_group (socket_general, mcast_addr, FALSE, NULL, - &err)) - g_error ("Couldn't join multicast group: %s", err->message); - g_clear_error (&err); - } - - event_saddr = g_inet_socket_address_new (mcast_addr, PTP_EVENT_PORT); - general_saddr = g_inet_socket_address_new (mcast_addr, PTP_GENERAL_PORT); - - /* Create socket sources */ - socket_event_source = - g_socket_create_source (socket_event, G_IO_IN | G_IO_PRI, NULL); - g_source_set_priority (socket_event_source, G_PRIORITY_HIGH); - g_source_set_callback (socket_event_source, (GSourceFunc) have_socket_data_cb, - NULL, NULL); - g_source_attach (socket_event_source, NULL); - socket_general_source = - g_socket_create_source (socket_general, G_IO_IN | G_IO_PRI, NULL); - g_source_set_priority (socket_general_source, G_PRIORITY_DEFAULT); - g_source_set_callback (socket_general_source, - (GSourceFunc) have_socket_data_cb, NULL, NULL); - g_source_attach (socket_general_source, NULL); - - g_strfreev (probed_ifaces); -} - -static void -drop_privileges (void) -{ -#ifdef HAVE_PTP_HELPER_SETUID - /* Switch to the given user/group */ -#ifdef HAVE_PTP_HELPER_SETUID_GROUP - { - struct group *grp; - - grp = getgrnam (HAVE_PTP_HELPER_SETUID_GROUP); - if (!grp) - g_error ("Failed to get group information '%s': %s", - HAVE_PTP_HELPER_SETUID_GROUP, g_strerror (errno)); - - if (setgid (grp->gr_gid) != 0) - g_error ("Failed to change to group '%s': %s", - HAVE_PTP_HELPER_SETUID_GROUP, g_strerror (errno)); - } -#endif - -#ifdef HAVE_PTP_HELPER_SETUID_USER - { - struct passwd *pwd; - - pwd = getpwnam (HAVE_PTP_HELPER_SETUID_USER); - if (!pwd) - g_error ("Failed to get user information '%s': %s", - HAVE_PTP_HELPER_SETUID_USER, g_strerror (errno)); - -#ifndef HAVE_PTP_HELPER_SETUID_GROUP - if (setgid (pwd->pw_gid) != 0) - g_error ("Failed to change to user group '%s': %s", - HAVE_PTP_HELPER_SETUID_USER, g_strerror (errno)); -#endif - - if (setuid (pwd->pw_uid) != 0) - g_error ("Failed to change to user '%s': %s", HAVE_PTP_HELPER_SETUID_USER, - g_strerror (errno)); - } -#endif -#endif -#ifdef HAVE_PTP_HELPER_CAPABILITIES - /* Drop all capabilities */ - { - cap_t caps; - - caps = cap_get_proc (); - if (caps == 0) - g_error ("Failed to get process caps: %s", g_strerror (errno)); - if (cap_clear (caps) != 0) - g_error ("Failed to clear caps: %s", g_strerror (errno)); - if (cap_set_proc (caps) != 0) - g_error ("Failed to set process caps: %s", g_strerror (errno)); - } -#endif -} - -static void -setup_stdio_channels (void) -{ - GSource *stdin_source; - - /* Create stdin source */ - stdin_channel = g_io_channel_unix_new (STDIN_FILENO); - if (g_io_channel_set_encoding (stdin_channel, NULL, - NULL) == G_IO_STATUS_ERROR) - g_error ("Failed to set stdin to binary encoding"); - g_io_channel_set_buffered (stdin_channel, FALSE); - stdin_source = - g_io_create_watch (stdin_channel, G_IO_IN | G_IO_PRI | G_IO_HUP); - g_source_set_priority (stdin_source, G_PRIORITY_DEFAULT); - g_source_set_callback (stdin_source, (GSourceFunc) have_stdin_data_cb, NULL, - NULL); - g_source_attach (stdin_source, NULL); - - /* Create stdout channel */ - stdout_channel = g_io_channel_unix_new (STDOUT_FILENO); - if (g_io_channel_set_encoding (stdout_channel, NULL, - NULL) == G_IO_STATUS_ERROR) - g_error ("Failed to set stdout to binary encoding"); - g_io_channel_set_buffered (stdout_channel, FALSE); -} - -static void -write_clock_id (void) -{ - GError *err = NULL; - GIOStatus status; - StdIOHeader header = { 0, }; - gsize written; - - /* Write clock id to stdout */ - - header.type = TYPE_CLOCK_ID; - header.size = 8; - status = - g_io_channel_write_chars (stdout_channel, (gchar *) & header, - sizeof (header), &written, &err); - if (status == G_IO_STATUS_ERROR) { - g_error ("Failed to write to stdout: %s", err->message); - g_clear_error (&err); - } else if (status == G_IO_STATUS_EOF) { - g_message ("EOF on stdout"); - exit (0); - } else if (status != G_IO_STATUS_NORMAL) { - g_error ("Unexpected stdout write status: %d", status); - } else if (written != sizeof (header)) { - g_error ("Unexpected write size: %" G_GSIZE_FORMAT, written); - } - - status = - g_io_channel_write_chars (stdout_channel, - (const gchar *) clock_id_array, sizeof (clock_id_array), &written, &err); - if (status == G_IO_STATUS_ERROR) { - g_error ("Failed to write to stdout: %s", err->message); - g_clear_error (&err); - } else if (status == G_IO_STATUS_EOF) { - g_message ("EOF on stdout"); - exit (0); - } else if (status != G_IO_STATUS_NORMAL) { - g_error ("Unexpected stdout write status: %d", status); - } else if (written != sizeof (clock_id_array)) { - g_error ("Unexpected write size: %" G_GSIZE_FORMAT, written); - } -} - -#ifdef __APPLE__ -static gint -dummy_poll (GPollFD * fds, guint nfds, gint timeout) -{ - return g_poll (fds, nfds, timeout); -} -#endif - -gint -main (gint argc, gchar ** argv) -{ - GOptionContext *opt_ctx; - GMainLoop *loop; - GError *err = NULL; - - /* FIXME: Work around some side effects of the changes from - * https://bugzilla.gnome.org/show_bug.cgi?id=741054 - * - * The modified poll function somehow calls setugid(), which - * then abort()s the application. Make sure that we use g_poll() - * here! - */ -#ifdef __APPLE__ - { - GMainContext *context = g_main_context_default (); - g_main_context_set_poll_func (context, dummy_poll); - } -#endif - -#ifdef HAVE_PTP_HELPER_SETUID - if (setuid (0) < 0) - g_error ("not running with superuser privileges"); -#endif - - opt_ctx = g_option_context_new ("- GStreamer PTP helper process"); - g_option_context_add_main_entries (opt_ctx, opt_entries, NULL); - if (!g_option_context_parse (opt_ctx, &argc, &argv, &err)) - g_error ("Error parsing options: %s", err->message); - g_clear_error (&err); - g_option_context_free (opt_ctx); - - setup_sockets (); - drop_privileges (); - setup_stdio_channels (); - write_clock_id (); - - /* Get running */ - loop = g_main_loop_new (NULL, FALSE); - g_main_loop_run (loop); - - /* We never exit cleanly, so don't do cleanup */ - g_assert_not_reached (); - - return 0; -} diff --git a/libs/gst/helpers/gst_gdb.py b/libs/gst/helpers/gst_gdb.py deleted file mode 100644 index 90374fba97..0000000000 --- a/libs/gst/helpers/gst_gdb.py +++ /dev/null @@ -1,1277 +0,0 @@ -# GStreamer -# Copyright (C) 2018 Pengutronix, Michael Olbrich <m.olbrich@pengutronix.de> -# -# gst_gdb.py: gdb extension for GStreamer -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Library General Public -# License as published by the Free Software Foundation; either -# version 2 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Library General Public License for more details. -# -# You should have received a copy of the GNU Library General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -# Boston, MA 02110-1301, USA. - -import gdb -import sys -import re - -from glib_gobject_helper import g_type_to_name, g_type_name_from_instance, \ - g_type_to_typenode, g_quark_to_string - -if sys.version_info[0] >= 3: - long = int - - -def is_gst_type(val, klass): - def _is_gst_type(type): - if str(type) == klass: - return True - - while type.code == gdb.TYPE_CODE_TYPEDEF: - type = type.target() - - if type.code != gdb.TYPE_CODE_STRUCT: - return False - - fields = type.fields() - if len(fields) < 1: - return False - - first_field = fields[0] - return _is_gst_type(first_field.type) - - type = val.type - if type.code != gdb.TYPE_CODE_PTR: - return False - type = type.target() - return _is_gst_type(type) - - -class GstMiniObjectPrettyPrinter: - "Prints a GstMiniObject instance pointer" - - def __init__(self, val): - self.val = val - - def to_string(self): - try: - inst = self.val.cast(gdb.lookup_type("GstMiniObject").pointer()) - gtype = inst["type"] - name = g_type_to_name(gtype) - return "0x%x [%s]" % (long(self.val), name) - except RuntimeError: - return "0x%x" % long(self.val) - - -class GstObjectPrettyPrinter: - "Prints a GstObject instance" - - def __init__(self, val): - self.val = val - - def to_string(self): - try: - name = g_type_name_from_instance(self.val) - if not name: - name = str(self.val.type.target()) - if long(self.val) != 0: - inst = self.val.cast(gdb.lookup_type("GstObject").pointer()) - inst_name = inst["name"].string() - if inst_name: - name += "|" + inst_name - return ("0x%x [%s]") % (long(self.val), name) - except RuntimeError: - return "0x%x" % long(self.val) - - -GST_SECOND = 1000000000 -GST_CLOCK_TIME_NONE = 2**64-1 -GST_CLOCK_STIME_NONE = -2**63 - - -def format_time(n, signed=False): - prefix = "" - invalid = False - if signed: - if n == GST_CLOCK_STIME_NONE: - invalid = True - prefix = "+" if n >= 0 else "-" - n = abs(n) - else: - if n == GST_CLOCK_TIME_NONE: - invalid = True - - if invalid: - return "99:99:99.999999999" - - return "%s%u:%02u:%02u.%09u" % ( - prefix, - n / (GST_SECOND * 60 * 60), - (n / (GST_SECOND * 60)) % 60, - (n / GST_SECOND) % 60, - n % GST_SECOND) - - -def format_time_value(val): - return format_time(int(val), str(val.type) == "GstClockTimeDiff") - - -class GstClockTimePrinter: - "Prints a GstClockTime / GstClockTimeDiff" - - def __init__(self, val): - self.val = val - - def to_string(self): - return "%d [%s]" % (int(self.val), format_time_value(self.val)) - - -def gst_pretty_printer_lookup(val): - if is_gst_type(val, "GstMiniObject"): - return GstMiniObjectPrettyPrinter(val) - if is_gst_type(val, "GstObject"): - return GstObjectPrettyPrinter(val) - if str(val.type) == "GstClockTime" or str(val.type) == "GstClockTimeDiff": - return GstClockTimePrinter(val) - return None - - -def save_memory_access(fallback): - def _save_memory_access(func): - def wrapper(*args, **kwargs): - try: - return func(*args, **kwargs) - except gdb.MemoryError: - return fallback - return wrapper - return _save_memory_access - - -def save_memory_access_print(message): - def _save_memory_access_print(func): - def wrapper(*args, **kwargs): - try: - func(*args, **kwargs) - except gdb.MemoryError: - _gdb_write(args[1], message) - return wrapper - return _save_memory_access_print - - -def _g_type_from_instance(instance): - if long(instance) != 0: - try: - inst = instance.cast(gdb.lookup_type("GTypeInstance").pointer()) - klass = inst["g_class"] - gtype = klass["g_type"] - return gtype - except RuntimeError: - pass - return None - - -def g_inherits_type(val, typename): - if is_gst_type(val, "GstObject"): - gtype = _g_type_from_instance(val) - if gtype is None: - return False - typenode = g_type_to_typenode(gtype) - elif is_gst_type(val, "GstMiniObject"): - mini = val.cast(gdb.lookup_type("GstMiniObject").pointer()) - try: - typenode = mini["type"].cast(gdb.lookup_type("TypeNode").pointer()) - except gdb.MemoryError: - return False - else: - return False - - for i in range(typenode["n_supers"]): - if g_type_to_name(typenode["supers"][i]) == typename: - return True - return False - - -def gst_is_bin(val): - return g_inherits_type(val, "GstBin") - - -def _g_array_iter(array, element_type): - if array == 0: - return - try: - item = array["data"].cast(element_type.pointer()) - for i in range(int(array["len"])): - yield item[i] - except gdb.MemoryError: - pass - - -def _g_value_get_value(val): - typenode = g_type_to_typenode(val["g_type"]) - if not typenode: - return None - tname = g_quark_to_string(typenode["qname"]) - fname = g_type_to_name(typenode["supers"][int(typenode["n_supers"])]) - if fname in ("gchar", "guchar", "gboolean", "gint", "guint", "glong", - "gulong", "gint64", "guint64", "gfloat", "gdouble", - "gpointer", "GFlags"): - try: - t = gdb.lookup_type(tname).pointer() - except RuntimeError: - t = gdb.lookup_type(fname).pointer() - elif fname == "gchararray": - t = gdb.lookup_type("char").pointer().pointer() - elif fname == "GstBitmask": - t = gdb.lookup_type("guint64").pointer() - elif fname == "GstFlagSet": - t = gdb.lookup_type("guint").pointer().pointer() - return val["data"].cast(t) - elif fname == "GstFraction": - t = gdb.lookup_type("gint").pointer().pointer() - return val["data"].cast(t) - elif fname == "GstFractionRange": - t = gdb.lookup_type("GValue").pointer().pointer() - elif fname == "GstValueList": - t = gdb.lookup_type("GArray").pointer().pointer() - elif fname in ("GBoxed", "GObject"): - try: - t = gdb.lookup_type(tname).pointer().pointer() - except RuntimeError: - t = gdb.lookup_type(tname).pointer().pointer() - else: - return val["data"] - - return val["data"].cast(t).dereference() - - -def gst_object_from_value(value): - if value.type.code != gdb.TYPE_CODE_PTR: - value = value.address - - if not is_gst_type(value, "GstObject"): - raise Exception("'%s' is not a GstObject" % args[0]) - - return value.cast(gdb.lookup_type("GstObject").pointer()) - - -def gst_object_pipeline(obj): - try: - while obj["parent"] != 0: - tmp = obj["parent"] - # sanity checks to handle memory corruption - if g_inherits_type(obj, "GstElement") and \ - GdbGstElement(obj) not in GdbGstElement(tmp).children(): - break - if g_inherits_type(obj, "GstPad"): - pad = GdbGstPad(obj) - if g_inherits_type(tmp, "GstElement"): - if pad not in GdbGstElement(tmp).pads(): - break - elif g_inherits_type(tmp, "GstProxyPad"): - t = gdb.lookup_type("GstProxyPad").pointer() - if pad != GdbGstPad(tmp.cast(t)["priv"]["internal"]): - break - obj = tmp - except gdb.MemoryError: - pass - - if not g_inherits_type(obj, "GstElement"): - raise Exception("Toplevel parent is not a GstElement") - return obj.cast(gdb.lookup_type("GstElement").pointer()) - - -def element_state_to_name(state): - names = [ - "VOID_PENDING", - "NULL", - "READY", - "PAUSED", - "PLAYING"] - return names[state] if state < len(names) else "UNKNOWN" - - -def task_state_to_name(state): - names = [ - "STARTED", - "STOPPED", - "PAUSED"] - return names[state] if state < len(names) else "UNKNOWN" - - -def _gdb_write(indent, text): - gdb.write("%s%s\n" % (" " * indent, text)) - - -class GdbCapsFeatures: - def __init__(self, val): - self.val = val - - def size(self): - if long(self.val) == 0: - return 0 - return int(self.val["array"]["len"]) - - def items(self): - if long(self.val) == 0: - return - for q in _g_array_iter(self.val["array"], gdb.lookup_type("GQuark")): - yield q - - def __eq__(self, other): - if self.size() != other.size(): - return False - a1 = list(self.items()) - a2 = list(other.items()) - for item in a1: - if item not in a2: - return False - return True - - def __str__(self): - if long(self.val) == 0: - return "" - count = self.size() - if int(self.val["is_any"]) == 1 and count == 0: - return "(ANY)" - if count == 0: - return "" - s = "" - for f in self.items(): - ss = g_quark_to_string(f) - if ss != "memory:SystemMemory" or count > 1: - s += ", " if s else "" - s += ss - return s - - -class GdbGstCaps: - def __init__(self, val): - self.val = val.cast(gdb.lookup_type("GstCapsImpl").pointer()) - - def size(self): - return int(self.val["array"]["len"]) - - def items(self): - gdb_type = gdb.lookup_type("GstCapsArrayElement") - for f in _g_array_iter(self.val["array"], gdb_type): - yield(GdbCapsFeatures(f["features"]), - GdbGstStructure(f["structure"])) - - def __eq__(self, other): - if self.size() != other.size(): - return False - a1 = list(self.items()) - a2 = list(other.items()) - for i in range(self.size()): - if a1[i] != a2[i]: - return False - return True - - def dot(self): - if self.size() == 0: - return "ANY" - s = "" - for (features, structure) in self.items(): - s += structure.name() - tmp = str(features) - if tmp: - s += "(" + tmp + ")" - s += "\\l" - if structure.size() > 0: - s += "\\l".join(structure.value_strings(" %18s: %s")) + "\\l" - return s - - @save_memory_access_print("<inaccessible memory>") - def print(self, indent, prefix=""): - items = list(self.items()) - if len(items) != 1: - _gdb_write(indent, prefix) - prefix = "" - for (features, structure) in items: - s = "%s %s" % (prefix, structure.name()) - tmp = str(features) - if tmp: - s += "(" + tmp + ")" - _gdb_write(indent, s) - for val in structure.value_strings("%s: %s", False): - _gdb_write(indent+1, val) - return s - - -class GdbGValue: - def __init__(self, val): - self.val = val - - def fundamental_typename(self): - typenode = g_type_to_typenode(self.val["g_type"]) - if not typenode: - return None - return g_type_to_name(typenode["supers"][int(typenode["n_supers"])]) - - def value(self): - return _g_value_get_value(self.val) - - def __str__(self): - try: - value = self.value() - tname = self.fundamental_typename() - gvalue_type = gdb.lookup_type("GValue") - if tname == "GstFraction": - v = "%d/%d" % (value[0], value[1]) - elif tname == "GstBitmask": - v = "0x%016x" % long(value) - elif tname == "gboolean": - v = "false" if int(value) == 0 else "true" - elif tname == "GstFlagSet": - v = "%x:%x" % (value[0], value[1]) - elif tname == "GstIntRange": - rmin = int(value[0]["v_uint64"]) >> 32 - rmax = int(value[0]["v_uint64"]) & 0xffffffff - step = int(value[1]["v_int"]) - if step == 1: - v = "[ %d, %d ]" % (rmin, rmax) - else: - v = "[ %d, %d, %d ]" % (rmin*step, rmax*step, step) - elif tname == "GstFractionRange": - v = "[ %s, %s ]" % (GdbGValue(value[0]), GdbGValue(value[1])) - elif tname in ("GstValueList", "GstValueArray"): - if gvalue_type.fields()[1].type == value.type: - gdb_type = gdb.lookup_type("GArray").pointer() - value = value[0]["v_pointer"].cast(gdb_type) - v = "<" - for l in _g_array_iter(value, gvalue_type): - v += " " if v == "<" else ", " - v += str(GdbGValue(l)) - v += " >" - elif tname in ("GEnum"): - v = "%s(%s)" % ( - g_type_to_name(g_type_to_typenode(self.val["g_type"])), - value["v_int"]) - else: - try: - v = value.string() - except RuntimeError: - # it is not a string-like type - if gvalue_type.fields()[1].type == value.type: - # don't print the raw GValue union - v = "<unknown type: %s>" % tname - else: - v = str(value) - except gdb.MemoryError: - v = "<inaccessible memory at 0x%x>" % int(self.val) - return v - - def __eq__(self, other): - return self.val == other.val - - -class GdbGstStructure: - def __init__(self, val): - self.val = val.cast(gdb.lookup_type("GstStructureImpl").pointer()) - - @save_memory_access("<inaccessible memory>") - def name(self): - return g_quark_to_string(self.val["s"]["name"]) - - @save_memory_access(0) - def size(self): - return int(self.val["fields_len"]) - - def values(self): - item = self.val["fields"].cast(gdb.lookup_type("GstStructureField").pointer()) - for i in range(self.size()): - f = item[i] - key = g_quark_to_string(f["name"]) - value = GdbGValue(f["value"]) - yield(key, value) - - def value(self, key): - for (k, value) in self.values(): - if k == key: - return value - raise KeyError(key) - - def __eq__(self, other): - if self.size() != other.size(): - return False - a1 = list(self.values()) - a2 = list(other.values()) - for (key, value) in a1: - if (key, value) not in a2: - return False - return True - - def value_strings(self, pattern, elide=True): - s = [] - for (key, value) in self.values(): - v = str(value) - if elide and len(v) > 25: - if v[0] in "[(<\"": - v = v[:20] + "... " + v[-1:] - else: - v = v[:22] + "..." - s.append(pattern % (key, v)) - return s - - @save_memory_access_print("<inaccessible memory>") - def print(self, indent, prefix=None): - if prefix is not None: - _gdb_write(indent, "%s: %s" % (prefix, self.name())) - else: - _gdb_write(indent, "%s:" % (self.name())) - for (key, value) in self.values(): - _gdb_write(indent+1, "%s: %s" % (key, str(value))) - - -class GdbGstSegment: - def __init__(self, val): - self.val = val - self.fmt = str(self.val["format"]).split("_")[-1].lower() - - def format_value(self, n): - if self.fmt == "time": - return format_time(n, False) - else: - return str(n) - - def print_optional(self, indent, key, skip=None): - value = int(self.val[key]) - if skip is None or value != skip: - _gdb_write(indent, "%s:%s %s" % - (key, (8-len(key))*" ", self.format_value(value))) - - def print(self, indent, seqnum=None): - s = "segment:" - if seqnum: - s += "(seqnum: %s)" % seqnum - _gdb_write(indent, s) - rate = float(self.val["rate"]) - applied_rate = float(self.val["applied_rate"]) - if applied_rate != 1.0: - applied = "(applied rate: %g)" % applied_rate - else: - applied = "" - _gdb_write(indent+1, "rate: %g%s" % (rate, applied)) - self.print_optional(indent+1, "base", 0) - self.print_optional(indent+1, "offset", 0) - self.print_optional(indent+1, "start") - self.print_optional(indent+1, "stop", GST_CLOCK_TIME_NONE) - self.print_optional(indent+1, "time") - self.print_optional(indent+1, "position") - self.print_optional(indent+1, "duration", GST_CLOCK_TIME_NONE) - - -class GdbGstEvent: - def __init__(self, val): - self.val = val.cast(gdb.lookup_type("GstEventImpl").pointer()) - - @save_memory_access("<inaccessible memory>") - def typestr(self): - t = self.val["event"]["type"] - (event_quarks, _) = gdb.lookup_symbol("event_quarks") - event_quarks = event_quarks.value() - i = 0 - while event_quarks[i]["name"] != 0: - if t == event_quarks[i]["type"]: - return event_quarks[i]["name"].string() - i += 1 - return None - - def structure(self): - return GdbGstStructure(self.val["structure"]) - - @save_memory_access_print("<inaccessible memory>") - def print(self, indent): - typestr = self.typestr() - seqnum = self.val["event"]["seqnum"] - if typestr == "caps": - caps = GdbGstCaps(self.structure().value("caps").value()) - caps.print(indent, "caps (seqnum: %s):" % seqnum) - elif typestr == "stream-start": - stream_id = self.structure().value("stream-id").value() - _gdb_write(indent, "stream-start: (seqnum %s)" % seqnum) - _gdb_write(indent + 1, "stream-id: %s" % stream_id.string()) - elif typestr == "segment": - segment = self.structure().value("segment").value() - GdbGstSegment(segment).print(indent, seqnum) - elif typestr == "tag": - struct = self.structure() - # skip 'GstTagList-' - name = struct.name()[11:] - t = gdb.lookup_type("GstTagListImpl").pointer() - s = struct.value("taglist").value().cast(t)["structure"] - structure = GdbGstStructure(s) - _gdb_write(indent, "tag: %s (seqnum: %s)" % (name, seqnum)) - for (key, value) in structure.values(): - _gdb_write(indent+1, "%s: %s" % (key, str(value))) - else: - self.structure().print(indent, "%s (seqnum: %s)" % (typestr, seqnum)) - - -class GdbGstBuffer: - def __init__(self, val): - self.val = val.cast(gdb.lookup_type("GstBuffer").pointer()) - - def print_optional(self, indent, key, skip=None, format_func=str): - value = int(self.val[key]) - if skip is None or value != skip: - _gdb_write(indent, "%s:%s %s" % - (key, (8-len(key))*" ", format_func(value))) - - @save_memory_access_print("<inaccessible memory>") - def print(self, indent): - _gdb_write(indent, "GstBuffer: (0x%x)" % self.val) - indent += 1 - self.print_optional(indent, "pool", 0) - self.print_optional(indent, "pts", GST_CLOCK_TIME_NONE, format_time) - self.print_optional(indent, "dts", GST_CLOCK_TIME_NONE, format_time) - self.print_optional(indent, "duration", GST_CLOCK_TIME_NONE, format_time) - self.print_optional(indent, "offset", GST_CLOCK_TIME_NONE) - self.print_optional(indent, "offset_end", GST_CLOCK_TIME_NONE) - - impl = self.val.cast(gdb.lookup_type("GstBufferImpl").pointer()) - meta_item = impl['item'] - if meta_item: - _gdb_write(indent, "Metas:") - indent += 1 - while meta_item: - meta = meta_item['meta'] - meta_type_name = g_type_to_name(meta['info']['type']) - _gdb_write(indent, "%s:" % meta_type_name) - indent += 1 - meta_info = str(meta.cast(gdb.lookup_type(meta_type_name))) - for l in meta_info.split('\n'): - _gdb_write(indent, l) - indent -= 1 - meta_item = meta_item['next'] - else: - _gdb_write(indent, "(No meta)") - - -class GdbGstQuery: - def __init__(self, val): - self.val = val.cast(gdb.lookup_type("GstQueryImpl").pointer()) - - @save_memory_access("<inaccessible memory>") - def typestr(self): - t = self.val["query"]["type"] - (query_quarks, _) = gdb.lookup_symbol("query_quarks") - query_quarks = query_quarks.value() - i = 0 - while query_quarks[i]["name"] != 0: - if t == query_quarks[i]["type"]: - return query_quarks[i]["name"].string() - i += 1 - return None - - def structure(self): - return GdbGstStructure(self.val["structure"]) - - @save_memory_access_print("<inaccessible memory>") - def print(self, indent): - typestr = self.typestr() - self.structure().print(indent, typestr) - - -class GdbGstObject: - def __init__(self, klass, val): - self.val = val.cast(klass) - - @save_memory_access("<inaccessible memory>") - def name(self): - obj = self.val.cast(gdb.lookup_type("GstObject").pointer()) - return obj["name"].string() - - def full_name(self): - parent = self.parent_element() - return "%s%s" % (parent.name() + ":" if parent else "", self.name()) - - def dot_name(self): - ptr = self.val.cast(gdb.lookup_type("void").pointer()) - return re.sub('[^a-zA-Z0-9<>]', '_', "%s_%s" % (self.name(), str(ptr))) - - def parent(self): - obj = self.val.cast(gdb.lookup_type("GstObject").pointer()) - return obj["parent"] - - def parent_element(self): - p = self.parent() - if p != 0 and g_inherits_type(p, "GstElement"): - element = p.cast(gdb.lookup_type("GstElement").pointer()) - return GdbGstElement(element) - return None - - def parent_pad(self): - p = self.parent() - if p != 0 and g_inherits_type(p, "GstPad"): - pad = p.cast(gdb.lookup_type("GstPad").pointer()) - return GdbGstPad(pad) - return None - - -class GdbGstPad(GdbGstObject): - def __init__(self, val): - gdb_type = gdb.lookup_type("GstPad").pointer() - super(GdbGstPad, self).__init__(gdb_type, val) - - def __eq__(self, other): - return self.val == other.val - - def is_linked(self): - return long(self.val["peer"]) != 0 - - def peer(self): - return GdbGstPad(self.val["peer"]) - - def direction(self): - return str(self.val["direction"]) - - def events(self): - if long(self.val["priv"]) == 0: - return - array = self.val["priv"]["events"] - for ev in _g_array_iter(array, gdb.lookup_type("PadEvent")): - yield GdbGstEvent(ev["event"]) - - def caps(self): - for ev in self.events(): - if ev.typestr() != "caps": - continue - return GdbGstCaps(ev.structure().value("caps").value()) - return None - - def template_caps(self): - tmp = self.val["padtemplate"] - return GdbGstCaps(tmp["caps"]) if int(tmp) != 0 else None - - def mode(self): - m = str(self.val["mode"]).split("_")[-1].lower() - if m in ("push", "pull"): - return m - return None - - def pad_type(self): - s = str(self.val["direction"]).split("_")[-1].capitalize() - if g_inherits_type(self.val, "GstGhostPad"): - s += "Ghost" - return s + "Pad" - - @save_memory_access_print("Pad(<inaccessible memory>)") - def print(self, indent): - m = ", " + self.mode() if self.mode() else "" - _gdb_write(indent, "%s(%s%s) {" % (self.pad_type(), self.name(), m)) - first = True - for ev in self.events(): - if first: - _gdb_write(indent+1, "events:") - first = False - ev.print(indent+2) - - if self.is_linked(): - real = self.peer().parent_pad() - _gdb_write(indent+1, "peer: %s" % - (real.full_name() if real else self.peer().full_name())) - - if g_inherits_type(self.val, "GstGhostPad"): - t = gdb.lookup_type("GstProxyPad").pointer() - internal = GdbGstPad(self.val.cast(t)["priv"]["internal"]) - if internal and internal.peer(): - _gdb_write(indent+1, "inner peer: %s" % - internal.peer().full_name()) - - task = self.val["task"] - if long(task) != 0: - _gdb_write(indent+1, "task: %s" % - task_state_to_name(int(task["state"]))) - - offset = long(self.val["offset"]) - if offset != 0: - _gdb_write(indent+1, "offset: %d [%s]" % - (offset, format_time(offset, True))) - - _gdb_write(indent, "}") - - def _dot(self, color, pname, indent): - spc = " " * indent - activation_mode = "-><" - style = "filled,solid" - template = self.val["padtemplate"] - if template != 0: - presence = template["presence"] - if str(presence) == "GST_PAD_SOMETIMES": - style = "filled,dotted" - if str(presence) == "GST_PAD_REQUEST": - style = "filled,dashed" - task_mode = "" - task = self.val["task"] - if long(task) != 0: - task_state = int(task["state"]) - if task_state == 0: # started - task_mode = "[T]" - if task_state == 2: # paused - task_mode = "[t]" - f = int(self.val["object"]["flags"]) - flags = "B" if f & 16 else "b" # GST_PAD_FLAG_BLOCKED - flags += "F" if f & 32 else "f" # GST_PAD_FLAG_FLUSHING - flags += "B" if f & 16 else "b" # GST_PAD_FLAG_BLOCKING - - s = "%s %s_%s [color=black, fillcolor=\"%s\", " \ - "label=\"%s%s\\n[%c][%s]%s\", height=\"0.2\", style=\"%s\"];\n" % \ - (spc, pname, self.dot_name(), color, self.name(), "", - activation_mode[int(self.val["mode"])], flags, task_mode, style) - return s - - def dot(self, indent): - spc = " " * indent - direction = self.direction() - element = self.parent_element() - ename = element.dot_name() if element else "" - s = "" - if g_inherits_type(self.val, "GstGhostPad"): - if direction == "GST_PAD_SRC": - color = "#ffdddd" - elif direction == "GST_PAD_SINK": - color = "#ddddff" - else: - color = "#ffffff" - - t = gdb.lookup_type("GstProxyPad").pointer() - other = GdbGstPad(self.val.cast(t)["priv"]["internal"]) - if other: - s += other._dot(color, "", indent) - pname = self.dot_name() - other_element = other.parent_element() - other_ename = other_element.dot_name() if other_element else "" - other_pname = other.dot_name() - if direction == "GST_PAD_SRC": - s += "%s%s_%s -> %s_%s [style=dashed, minlen=0]\n" % \ - (spc, other_ename, other_pname, ename, pname) - else: - s += "%s%s_%s -> %s_%s [style=dashed, minlen=0]\n" % \ - (spc, ename, pname, other_ename, other_pname) - else: - if direction == "GST_PAD_SRC": - color = "#ffaaaa" - elif direction == "GST_PAD_SINK": - color = "#aaaaff" - else: - color = "#cccccc" - - s += self._dot(color, ename, indent) - return s - - def link_dot(self, indent, element): - spc = " " * indent - - peer = self.peer() - peer_element = peer.parent_element() - - caps = self.caps() - if not caps: - caps = self.template_caps() - peer_caps = peer.caps() - if not peer_caps: - peer_caps = peer.template_caps() - - pname = self.dot_name() - ename = element.dot_name() if element else "" - peer_pname = peer.dot_name() - peer_ename = peer_element.dot_name() if peer_element else "" - - if caps and peer_caps and caps == peer_caps: - s = "%s%s_%s -> %s_%s [label=\"%s\"]\n" % \ - (spc, ename, pname, peer_ename, peer_pname, caps.dot()) - elif caps and peer_caps and caps != peer_caps: - s = "%s%s_%s -> %s_%s [labeldistance=\"10\", labelangle=\"0\", " \ - % (spc, ename, pname, peer_ename, peer_pname) - s += "label=\"" + " "*50 + "\", " - if self.direction() == "GST_PAD_SRC": - media_src = caps.dot() - media_dst = peer_caps.dot() - else: - media_src = peer_caps.dot() - media_dst = caps.dot() - s += "taillabel=\"%s\", headlabel=\"%s\"]\n" % \ - (media_src, media_dst) - else: - s = "%s%s_%s -> %s_%s\n" % \ - (spc, ename, pname, peer_ename, peer_pname) - return s - - -class GdbGstElement(GdbGstObject): - def __init__(self, val): - gdb_type = gdb.lookup_type("GstElement").pointer() - super(GdbGstElement, self).__init__(gdb_type, val) - self.is_bin = gst_is_bin(self.val) - - def __eq__(self, other): - return self.val == other.val - - def children(self): - if not self.is_bin: - return - b = self.val.cast(gdb.lookup_type("GstBin").pointer()) - link = b["children"] - while link != 0: - yield GdbGstElement(link["data"]) - link = link["next"] - - def has_pads(self, pad_group="pads"): - return self.val[pad_group] != 0 - - def pads(self, pad_group="pads"): - link = self.val[pad_group] - while link != 0: - yield GdbGstPad(link["data"]) - link = link["next"] - - def _state_dot(self): - icons = "~0-=>" - current = int(self.val["current_state"]) - pending = int(self.val["pending_state"]) - if pending == 0: - # GST_ELEMENT_FLAG_LOCKED_STATE == 16 - locked = (int(self.val["object"]["flags"]) & 16) != 0 - return "\\n[%c]%s" % (icons[current], "(locked)" if locked else "") - return "\\n[%c] -> [%c]" % (icons[current], icons[pending]) - - @save_memory_access_print("Element(<inaccessible memory>)") - def print(self, indent): - _gdb_write(indent, "%s(%s) {" % - (g_type_name_from_instance(self.val), self.name())) - for p in self.pads(): - p.print(indent+2) - - first = True - for child in self.children(): - if first: - _gdb_write(indent+2, "children:") - first = False - _gdb_write(indent+3, child.name()) - - current_state = self.val["current_state"] - s = "state: %s" % element_state_to_name(current_state) - for var in ("pending", "target"): - state = self.val[var + "_state"] - if state > 0 and state != current_state: - s += ", %s: %s" % (var, element_state_to_name(state)) - _gdb_write(indent+2, s) - - _gdb_write(indent+2, "base_time: %s" % - format_time_value(self.val["base_time"])) - _gdb_write(indent+2, "start_time: %s" % - format_time_value(self.val["start_time"])) - - _gdb_write(indent, "}") - - @save_memory_access_print("<inaccessible memory>") - def print_tree(self, indent): - _gdb_write(indent, "%s(%s)" % (self.name(), self.val)) - for child in self.children(): - child.print_tree(indent+1) - - def _dot(self, indent=0): - spc = " " * indent - - s = "%ssubgraph cluster_%s {\n" % (spc, self.dot_name()) - s += "%s fontname=\"Bitstream Vera Sans\";\n" % spc - s += "%s fontsize=\"8\";\n" % spc - s += "%s style=\"filled,rounded\";\n" % spc - s += "%s color=black;\n" % spc - s += "%s label=\"%s\\n%s%s%s\";\n" % \ - (spc, g_type_name_from_instance(self.val), self.name(), - self._state_dot(), "") - - sink_name = None - if self.has_pads("sinkpads"): - (ss, sink_name) = self._dot_pads(indent+1, "sinkpads", - self.dot_name() + "_sink") - s += ss - src_name = None - if self.has_pads("srcpads"): - (ss, src_name) = self._dot_pads(indent+1, "srcpads", - self.dot_name() + "_src") - s += ss - if sink_name and src_name: - name = self.dot_name() - s += "%s %s_%s -> %s_%s [style=\"invis\"];\n" % \ - (spc, name, sink_name, name, src_name) - - if gst_is_bin(self.val): - s += "%s fillcolor=\"#ffffff\";\n" % spc - s += self.dot(indent+1) - else: - if src_name and not sink_name: - s += "%s fillcolor=\"#ffaaaa\";\n" % spc - elif not src_name and sink_name: - s += "%s fillcolor=\"#aaaaff\";\n" % spc - elif src_name and sink_name: - s += "%s fillcolor=\"#aaffaa\";\n" % spc - else: - s += "%s fillcolor=\"#ffffff\";\n" % spc - s += "%s}\n\n" % spc - - for p in self.pads(): - if not p.is_linked(): - continue - if p.direction() == "GST_PAD_SRC": - s += p.link_dot(indent, self) - else: - pp = p.peer() - if not g_inherits_type(pp.val, "GstGhostPad") and \ - g_inherits_type(pp.val, "GstProxyPad"): - s += pp.link_dot(indent, None) - return s - - def _dot_pads(self, indent, pad_group, cluster_name): - spc = " " * indent - s = "%ssubgraph cluster_%s {\n" % (spc, cluster_name) - s += "%s label=\"\";\n" % spc - s += "%s style=\"invis\";\n" % spc - name = None - for p in self.pads(pad_group): - s += p.dot(indent) - if not name: - name = p.dot_name() - s += "%s}\n\n" % spc - return(s, name) - - def dot(self, indent): - s = "" - for child in self.children(): - try: - s += child._dot(indent) - except gdb.MemoryError: - gdb.write("warning: inaccessible memory in element 0x%x\n" % - long(child.val)) - return s - - def pipeline_dot(self): - t = g_type_name_from_instance(self.val) - - s = "digraph pipeline {\n" - s += " rankdir=LR;\n" - s += " fontname=\"sans\";\n" - s += " fontsize=\"10\";\n" - s += " labelloc=t;\n" - s += " nodesep=.1;\n" - s += " ranksep=.2;\n" - s += " label=\"<%s>\\n%s%s%s\";\n" % (t, self.name(), "", "") - s += " node [style=\"filled,rounded\", shape=box, fontsize=\"9\", " \ - "fontname=\"sans\", margin=\"0.0,0.0\"];\n" - s += " edge [labelfontsize=\"6\", fontsize=\"9\", " \ - "fontname=\"monospace\"];\n" - s += " \n" - s += " legend [\n" - s += " pos=\"0,0!\",\n" - s += " margin=\"0.05,0.05\",\n" - s += " style=\"filled\",\n" - s += " label=\"Legend\\lElement-States: [~] void-pending, " \ - "[0] null, [-] ready, [=] paused, [>] playing\\l" \ - "Pad-Activation: [-] none, [>] push, [<] pull\\l" \ - "Pad-Flags: [b]locked, [f]lushing, [b]locking, [E]OS; " \ - "upper-case is set\\lPad-Task: [T] has started task, " \ - "[t] has paused task\\l\",\n" - s += " ];" - s += "\n" - - s += self.dot(1) - - s += "}\n" - - return s - - -class GstDot(gdb.Command): - """\ -Create a pipeline dot file as close as possible to the output of -GST_DEBUG_BIN_TO_DOT_FILE. This command will find the top-level parent -for the given gstreamer object and create the dot for that element. - -Usage: gst-dot <gst-object> <file-name>""" - def __init__(self): - super(GstDot, self).__init__("gst-dot", gdb.COMMAND_DATA) - - def invoke(self, arg, from_tty): - self.dont_repeat() - args = gdb.string_to_argv(arg) - if len(args) != 2: - raise Exception("Usage: gst-dot <gst-object> <file>") - - value = gdb.parse_and_eval(args[0]) - if not value: - raise Exception("'%s' is not a valid object" % args[0]) - - value = gst_object_from_value(value) - value = gst_object_pipeline(value) - - dot = GdbGstElement(value).pipeline_dot() - file = open(args[1], "w") - file.write(dot) - file.close() - - def complete(self, text, word): - cmd = gdb.string_to_argv(text) - if len(cmd) == 0 or(len(cmd) == 1 and len(word) > 0): - return gdb.COMPLETE_SYMBOL - return gdb.COMPLETE_FILENAME - - -class GstPrint(gdb.Command): - """\ -Print high-level information for GStreamer objects - -Usage gst-print <gstreamer-object>""" - def __init__(self): - super(GstPrint, self).__init__("gst-print", gdb.COMMAND_DATA, - gdb.COMPLETE_SYMBOL) - - def invoke(self, arg, from_tty): - value = gdb.parse_and_eval(arg) - if not value: - raise Exception("'%s' is not a valid object" % arg) - - if value.type.code != gdb.TYPE_CODE_PTR: - value = value.address - - if g_inherits_type(value, "GstElement"): - obj = GdbGstElement(value) - elif g_inherits_type(value, "GstPad"): - obj = GdbGstPad(value) - elif g_inherits_type(value, "GstCaps"): - obj = GdbGstCaps(value) - elif g_inherits_type(value, "GstEvent"): - obj = GdbGstEvent(value) - elif g_inherits_type(value, "GstQuery"): - obj = GdbGstQuery(value) - elif g_inherits_type(value, "GstBuffer"): - obj = GdbGstBuffer(value) - elif is_gst_type(value, "GstStructure"): - obj = GdbGstStructure(value) - else: - raise Exception("'%s' has an unknown type (%s)" % (arg, value)) - - obj.print(0) - - -class GstPipelineTree(gdb.Command): - """\ -Usage: gst-pipeline-tree <gst-object>""" - def __init__(self): - super(GstPipelineTree, self).__init__("gst-pipeline-tree", - gdb.COMPLETE_SYMBOL) - - def invoke(self, arg, from_tty): - self.dont_repeat() - args = gdb.string_to_argv(arg) - if len(args) != 1: - raise Exception("Usage: gst-pipeline-tree <gst-object>") - - value = gdb.parse_and_eval(args[0]) - if not value: - raise Exception("'%s' is not a valid object" % args[0]) - - value = gst_object_from_value(value) - value = gst_object_pipeline(value) - GdbGstElement(value).print_tree(0) - - -GstDot() -GstPrint() -GstPipelineTree() - - -class GstPipeline(gdb.Function): - """\ -Find the top-level pipeline for the given element""" - - def __init__(self): - super(GstPipeline, self).__init__("gst_pipeline") - - def invoke(self, arg): - value = gst_object_from_value(arg) - return gst_object_pipeline(value) - - -class GstBinGet(gdb.Function): - """\ -Find a child element with the given name""" - - def __init__(self): - super(GstBinGet, self).__init__("gst_bin_get") - - def find(self, obj, name, recurse): - for child in obj.children(): - if child.name() == name: - return child.val - if recurse: - result = self.find(child, name, recurse) - if result is not None: - return result - - def invoke(self, element, arg): - value = gst_object_from_value(element) - if not g_inherits_type(value, "GstElement"): - raise Exception("'%s' is not a GstElement" % - str(value.address)) - - try: - name = arg.string() - except gdb.error: - raise Exception("Usage: $gst_bin_get(<gst-object>, \"<name>\")") - - obj = GdbGstElement(value) - child = self.find(obj, name, False) - if child is None: - child = self.find(obj, name, True) - if child is None: - raise Exception("No child named '%s' found." % name) - return child - - -class GstElementPad(gdb.Function): - """\ -Get the pad with the given name""" - - def __init__(self): - super(GstElementPad, self).__init__("gst_element_pad") - - def invoke(self, element, arg): - value = gst_object_from_value(element) - if not g_inherits_type(value, "GstElement"): - raise Exception("'%s' is not a GstElement" % - str(value.address)) - - try: - name = arg.string() - except gdb.error: - raise Exception("Usage: $gst_element_pad(<gst-object>, \"<pad-name>\")") - - obj = GdbGstElement(value) - for pad in obj.pads(): - if pad.name() == name: - return pad.val - - raise Exception("No pad named '%s' found." % name) - - -GstPipeline() -GstBinGet() -GstElementPad() - - -def register(obj): - if obj is None: - obj = gdb - - # Make sure this is always used before the glib lookup function. - # Otherwise the gobject pretty printer is used for GstObjects - obj.pretty_printers.insert(0, gst_pretty_printer_lookup) diff --git a/libs/gst/helpers/libgstreamer-gdb.py.in b/libs/gst/helpers/libgstreamer-gdb.py.in deleted file mode 100644 index 2cf65429fa..0000000000 --- a/libs/gst/helpers/libgstreamer-gdb.py.in +++ /dev/null @@ -1,10 +0,0 @@ -import sys -import gdb - -# Update module path. -dir_ = '@DATADIR@/gstreamer-@GST_API_VERSION@/gdb' -if not dir_ in sys.path: - sys.path.insert(0, dir_) - -from gst_gdb import register -register (gdb.current_objfile ()) diff --git a/libs/gst/helpers/meson.build b/libs/gst/helpers/meson.build deleted file mode 100644 index b34423f9c2..0000000000 --- a/libs/gst/helpers/meson.build +++ /dev/null @@ -1,143 +0,0 @@ -executable('gst-plugin-scanner', - 'gst-plugin-scanner.c', - c_args : gst_c_args, - include_directories : [configinc], - dependencies : [gobject_dep, gmodule_dep, glib_dep, mathlib, gst_dep], - install_dir : helpers_install_dir, - install: true, -) - -# Used in test env setup to make tests find plugin scanner in build tree -gst_scanner_dir = meson.current_build_dir() - -if bashcomp_found - executable('gst-completion-helper', - 'gst-completion-helper.c', - c_args : gst_c_args, - include_directories : [configinc], - dependencies : [gobject_dep, glib_dep, gst_dep], - install_dir : helpers_install_dir, - install: true, - ) -endif - -# Check PTP support -have_ptp = false -if host_system == 'android' - message('PTP not supported on Android because of permissions.') -elif host_system == 'windows' - message('PTP not supported on Windows, not ported yet.') -elif host_system == 'ios' - message('PTP not supported on iOS because of permissions.') -elif ['linux', 'darwin', 'netbsd', 'freebsd', 'openbsd', 'kfreebsd', 'dragonfly', 'sunos', 'gnu', 'gnu/kfreebsd'].contains(host_system) - message('PTP supported on ' + host_system + '.') - have_ptp = true -else - message('PTP not supported on ' + host_system + ', not ported yet.') -endif - -if have_ptp - cdata.set('HAVE_PTP', 1, description : 'PTP support available') - - if cc.compiles('''#include <sys/ioctl.h> - #include <net/if.h> - int some_func (void) { - struct ifreq ifr; - struct ifconf ifc; - ioctl(0, SIOCGIFCONF, &ifc); - ioctl(0, SIOCGIFFLAGS, &ifr); - ioctl(0, SIOCGIFHWADDR, &ifr); - return ifr.ifr_hwaddr.sa_data[0]; - }''', - name : 'SIOCGIFCONF, SIOCGIFFLAGS and SIOCGIFHWADDR available') - cdata.set('HAVE_SIOCGIFCONF_SIOCGIFFLAGS_SIOCGIFHWADDR', 1, - description : 'SIOCGIFCONF, SIOCGIFFLAGS and SIOCGIFHWADDR is available') - endif - - if cc.compiles('''#include <ifaddrs.h> - #include <net/if.h> - #include <net/if_dl.h> - int some_func (void) { - struct ifaddrs *ifaddr; - getifaddrs(&ifaddr); - return (ifaddr->ifa_flags & IFF_LOOPBACK) && ifaddr->ifa_addr->sa_family != AF_LINK; - }''', name : 'getifaddrs() and AF_LINK available') - cdata.set('HAVE_GETIFADDRS_AF_LINK', 1, - description : 'getifaddrs() and AF_LINK is available') - endif - - setcap_prog = find_program('setcap', '/usr/sbin/setcap', '/sbin/setcap', required : false) - cap_dep = dependency('libcap', required: false) - - # user/group to change to in gst-ptp-helper - ptp_helper_setuid_user = get_option('ptp-helper-setuid-user') - if ptp_helper_setuid_user != '' - cdata.set_quoted('HAVE_PTP_HELPER_SETUID_USER', ptp_helper_setuid_user, - description : 'PTP helper setuid user') - endif - ptp_helper_setuid_group = get_option('ptp-helper-setuid-group') - if ptp_helper_setuid_group != '' - cdata.set_quoted('HAVE_PTP_HELPER_SETUID_GROUP', ptp_helper_setuid_group, - description : 'PTP helper setuid group') - endif - - # how to install gst-ptp-helper - with_ptp_helper_permissions = get_option('ptp-helper-permissions') - if with_ptp_helper_permissions == 'auto' - if setcap_prog.found() and cap_dep.found() - with_ptp_helper_permissions = 'capabilities' - else - with_ptp_helper_permissions = 'setuid-root' - endif - endif - message('How to install gst-ptp-helper: ' + with_ptp_helper_permissions) - - if with_ptp_helper_permissions == 'none' - # nothing to do - elif with_ptp_helper_permissions == 'setuid-root' - cdata.set('HAVE_PTP_HELPER_SETUID', 1, - description : 'Use setuid-root for permissions in PTP helper') - elif with_ptp_helper_permissions == 'capabilities' - if not setcap_prog.found() - error('capabilities-based ptp-helper-permissions requested, but could not find setcap tool.') - elif not cap_dep.found() - error('capabilities-based ptp-helper-permissions requested, but could not find libcap.') - endif - cdata.set('HAVE_PTP_HELPER_CAPABILITIES', 1, - description : 'Use capabilities for permissions in PTP helper') - else - error('Unexpected ptp helper permissions value: ' + with_ptp_helper_permissions) - endif - - executable('gst-ptp-helper', 'gst-ptp-helper.c', - c_args : gst_c_args, - include_directories : [configinc, libsinc], - dependencies : [gio_dep, gobject_dep, glib_dep, mathlib, gst_dep, cap_dep], - install_dir : helpers_install_dir, - install : true) - - meson.add_install_script('ptp_helper_post_install.sh', - helpers_install_dir, with_ptp_helper_permissions, - setcap_prog.found() ? setcap_prog.path() : '') -endif - -install_data(['gst_gdb.py', 'glib_gobject_helper.py'], - install_dir : join_paths(get_option('datadir'), 'gstreamer-@0@'.format(apiversion), 'gdb')) - -gdbconf = configuration_data() -gdbconf.set('GST_API_VERSION', apiversion) -gdbconf.set('DATADIR', '@0@/@1@'.format(get_option('prefix'), get_option('datadir'))) - -if host_system != 'windows' - # XXX: We add a leading './' because prefix is an absolute path and we - # need it to be a relative path so that join_paths appends it to the end. - gdb_install_dir = join_paths(get_option('datadir'), 'gdb', 'auto-load', './' + get_option('prefix'), get_option('libdir')) -else - # FIXME: Cannot install on Windows because the path will contain a drive - # letter and colons are not allowed in paths. - gdb_install_dir = disabler() -endif -configure_file(input : 'libgstreamer-gdb.py.in', - output : 'libgstreamer-@0@.so.@1@-gdb.py'.format(apiversion, libversion), - install_dir : gdb_install_dir, - configuration : gdbconf) diff --git a/libs/gst/helpers/ptp_helper_post_install.sh b/libs/gst/helpers/ptp_helper_post_install.sh deleted file mode 100755 index 4370acd956..0000000000 --- a/libs/gst/helpers/ptp_helper_post_install.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh -# Meson install script for gst-ptp-helper -# Fails silently at the moment if setting permissions/capabilities doesn't work -helpers_install_dir="$1" -with_ptp_helper_permissions="$2" -setcap="$3" - -ptp_helper="$MESON_INSTALL_DESTDIR_PREFIX/$helpers_install_dir/gst-ptp-helper" - -case "$with_ptp_helper_permissions" in - setuid-root) - echo "$0: permissions before: " - ls -l "$ptp_helper" - chown root "$ptp_helper" || true - chmod u+s "$ptp_helper" || true - echo "$0: permissions after: " - ls -l "$ptp_helper" - ;; - capabilities) - echo "Calling $setcap cap_net_bind_service,cap_net_admin+ep $ptp_helper" - $setcap cap_net_bind_service,cap_net_admin+ep "$ptp_helper" || true - ;; - none) - echo "No perms/caps to set for $ptp_helper" - ;; - *) - echo "$0 ERROR: unexpected permissions value '$with_ptp_helper_permissions'"; - exit 2; -esac diff --git a/libs/gst/meson.build b/libs/gst/meson.build deleted file mode 100644 index 33ecc2c274..0000000000 --- a/libs/gst/meson.build +++ /dev/null @@ -1,10 +0,0 @@ -subdir('base') -subdir('controller') -subdir('net') -subdir('helpers') - -if get_option('check').disabled() - gst_check_dep = dependency('', required : false) -else - subdir('check') -endif diff --git a/libs/gst/net/gstnet.h b/libs/gst/net/gstnet.h deleted file mode 100644 index 28173809c2..0000000000 --- a/libs/gst/net/gstnet.h +++ /dev/null @@ -1,31 +0,0 @@ -/* GStreamer - * Copyright (C) 2005 Andy Wingo <wingo@pobox.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - - -#ifndef __GST_NET_H__ -#define __GST_NET_H__ - -#include <gst/net/net-prelude.h> - -#include <gst/net/gstnetaddressmeta.h> -#include <gst/net/gstnetclientclock.h> -#include <gst/net/gstnettimepacket.h> -#include <gst/net/gstnettimeprovider.h> - -#endif /* __GST_NET_H__ */ diff --git a/libs/gst/net/gstnetaddressmeta.c b/libs/gst/net/gstnetaddressmeta.c deleted file mode 100644 index 9867cac980..0000000000 --- a/libs/gst/net/gstnetaddressmeta.c +++ /dev/null @@ -1,141 +0,0 @@ -/* GStreamer - * Copyright (C) <2011> Wim Taymans <wim.taymans@gmail.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * SECTION:gstnetaddressmeta - * @title: GstNetAddressMeta - * @short_description: Network address metadata - * - * #GstNetAddressMeta can be used to store a network address (a #GSocketAddress) - * in a #GstBuffer so that it network elements can track the to and from address - * of the buffer. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <string.h> - -#include "gstnetaddressmeta.h" - -static gboolean -net_address_meta_init (GstMeta * meta, gpointer params, GstBuffer * buffer) -{ - GstNetAddressMeta *nmeta = (GstNetAddressMeta *) meta; - - nmeta->addr = NULL; - - return TRUE; -} - -static gboolean -net_address_meta_transform (GstBuffer * transbuf, GstMeta * meta, - GstBuffer * buffer, GQuark type, gpointer data) -{ - GstNetAddressMeta *smeta, *dmeta; - smeta = (GstNetAddressMeta *) meta; - - /* we always copy no matter what transform */ - dmeta = gst_buffer_add_net_address_meta (transbuf, smeta->addr); - if (!dmeta) - return FALSE; - - return TRUE; -} - -static void -net_address_meta_free (GstMeta * meta, GstBuffer * buffer) -{ - GstNetAddressMeta *nmeta = (GstNetAddressMeta *) meta; - - if (nmeta->addr) - g_object_unref (nmeta->addr); - nmeta->addr = NULL; -} - -GType -gst_net_address_meta_api_get_type (void) -{ - static GType type; - static const gchar *tags[] = { "origin", NULL }; - - if (g_once_init_enter (&type)) { - GType _type = gst_meta_api_type_register ("GstNetAddressMetaAPI", tags); - g_once_init_leave (&type, _type); - } - return type; -} - -const GstMetaInfo * -gst_net_address_meta_get_info (void) -{ - static const GstMetaInfo *meta_info = NULL; - - if (g_once_init_enter ((GstMetaInfo **) & meta_info)) { - const GstMetaInfo *mi = gst_meta_register (GST_NET_ADDRESS_META_API_TYPE, - "GstNetAddressMeta", - sizeof (GstNetAddressMeta), - net_address_meta_init, - net_address_meta_free, net_address_meta_transform); - g_once_init_leave ((GstMetaInfo **) & meta_info, (GstMetaInfo *) mi); - } - return meta_info; -} - -/** - * gst_buffer_add_net_address_meta: - * @buffer: a #GstBuffer - * @addr: a @GSocketAddress to connect to @buffer - * - * Attaches @addr as metadata in a #GstNetAddressMeta to @buffer. - * - * Returns: (transfer none): a #GstNetAddressMeta connected to @buffer - */ -GstNetAddressMeta * -gst_buffer_add_net_address_meta (GstBuffer * buffer, GSocketAddress * addr) -{ - GstNetAddressMeta *meta; - - g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); - g_return_val_if_fail (G_IS_SOCKET_ADDRESS (addr), NULL); - - meta = - (GstNetAddressMeta *) gst_buffer_add_meta (buffer, - GST_NET_ADDRESS_META_INFO, NULL); - - meta->addr = g_object_ref (addr); - - return meta; -} - -/** - * gst_buffer_get_net_address_meta: - * @buffer: a #GstBuffer - * - * Find the #GstNetAddressMeta on @buffer. - * - * Returns: (transfer none): the #GstNetAddressMeta or %NULL when there - * is no such metadata on @buffer. - */ -GstNetAddressMeta * -gst_buffer_get_net_address_meta (GstBuffer * buffer) -{ - return (GstNetAddressMeta *) - gst_buffer_get_meta (buffer, GST_NET_ADDRESS_META_API_TYPE); -} diff --git a/libs/gst/net/gstnetaddressmeta.h b/libs/gst/net/gstnetaddressmeta.h deleted file mode 100644 index e949b3117e..0000000000 --- a/libs/gst/net/gstnetaddressmeta.h +++ /dev/null @@ -1,63 +0,0 @@ -/* GStreamer - * Copyright (C) <2011> Wim Taymans <wim.taymans@gmail.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_NET_ADDRESS_META_H__ -#define __GST_NET_ADDRESS_META_H__ - -#include <gst/gst.h> -#include <gio/gio.h> -#include <gst/net/net-prelude.h> - -G_BEGIN_DECLS - -typedef struct _GstNetAddressMeta GstNetAddressMeta; - -/** - * GstNetAddressMeta: - * @meta: the parent type - * @addr: a #GSocketAddress stored as metadata - * - * Buffer metadata for network addresses. - */ -struct _GstNetAddressMeta { - GstMeta meta; - - GSocketAddress *addr; -}; - -GST_NET_API -GType gst_net_address_meta_api_get_type (void); -#define GST_NET_ADDRESS_META_API_TYPE (gst_net_address_meta_api_get_type()) - -/* implementation */ - -GST_NET_API -const GstMetaInfo *gst_net_address_meta_get_info (void); -#define GST_NET_ADDRESS_META_INFO (gst_net_address_meta_get_info()) - -GST_NET_API -GstNetAddressMeta * gst_buffer_add_net_address_meta (GstBuffer *buffer, - GSocketAddress *addr); -GST_NET_API -GstNetAddressMeta * gst_buffer_get_net_address_meta (GstBuffer *buffer); - -G_END_DECLS - -#endif /* __GST_NET_ADDRESS_META_H__ */ - diff --git a/libs/gst/net/gstnetclientclock.c b/libs/gst/net/gstnetclientclock.c deleted file mode 100644 index 13ff01321e..0000000000 --- a/libs/gst/net/gstnetclientclock.c +++ /dev/null @@ -1,1492 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> - * 2005 Wim Taymans <wim@fluendo.com> - * 2005 Andy Wingo <wingo@pobox.com> - * Copyright (C) 2012 Collabora Ltd. <tim.muller@collabora.co.uk> - * Copyright (C) 2015 Sebastian Dröge <sebastian@centricular.com> - * - * gstnetclientclock.h: clock that synchronizes itself to a time provider over - * the network - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ -/** - * SECTION:gstnetclientclock - * @title: GstNetClientClock - * @short_description: Special clock that synchronizes to a remote time - * provider. - * @see_also: #GstClock, #GstNetTimeProvider, #GstPipeline - * - * #GstNetClientClock implements a custom #GstClock that synchronizes its time - * to a remote time provider such as #GstNetTimeProvider. #GstNtpClock - * implements a #GstClock that synchronizes its time to a remote NTPv4 server. - * - * A new clock is created with gst_net_client_clock_new() or - * gst_ntp_clock_new(), which takes the address and port of the remote time - * provider along with a name and an initial time. - * - * This clock will poll the time provider and will update its calibration - * parameters based on the local and remote observations. - * - * The "round-trip" property limits the maximum round trip packets can take. - * - * Various parameters of the clock can be configured with the parent #GstClock - * "timeout", "window-size" and "window-threshold" object properties. - * - * A #GstNetClientClock and #GstNtpClock is typically set on a #GstPipeline with - * gst_pipeline_use_clock(). - * - * If you set a #GstBus on the clock via the "bus" object property, it will - * send @GST_MESSAGE_ELEMENT messages with an attached #GstStructure containing - * statistics about clock accuracy and network traffic. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstnettimepacket.h" -#include "gstntppacket.h" -#include "gstnetclientclock.h" -#include "gstnetutils.h" - -#include <gio/gio.h> - -#include <string.h> - -GST_DEBUG_CATEGORY_STATIC (ncc_debug); -#define GST_CAT_DEFAULT (ncc_debug) - -typedef struct -{ - GstClock *clock; /* GstNetClientInternalClock */ - - GList *clocks; /* GstNetClientClocks */ - - GstClockID remove_id; -} ClockCache; - -G_LOCK_DEFINE_STATIC (clocks_lock); -static GList *clocks = NULL; - -#define GST_TYPE_NET_CLIENT_INTERNAL_CLOCK \ - (gst_net_client_internal_clock_get_type()) -#define GST_NET_CLIENT_INTERNAL_CLOCK(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_NET_CLIENT_INTERNAL_CLOCK,GstNetClientInternalClock)) -#define GST_NET_CLIENT_INTERNAL_CLOCK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_NET_CLIENT_INTERNAL_CLOCK,GstNetClientInternalClockClass)) -#define GST_IS_NET_CLIENT_INTERNAL_CLOCK(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_NET_CLIENT_INTERNAL_CLOCK)) -#define GST_IS_NET_CLIENT_INTERNAL_CLOCK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_NET_CLIENT_INTERNAL_CLOCK)) - -typedef struct _GstNetClientInternalClock GstNetClientInternalClock; -typedef struct _GstNetClientInternalClockClass GstNetClientInternalClockClass; - -G_GNUC_INTERNAL GType gst_net_client_internal_clock_get_type (void); - -#define DEFAULT_ADDRESS "127.0.0.1" -#define DEFAULT_PORT 5637 -#define DEFAULT_TIMEOUT GST_SECOND -#define DEFAULT_ROUNDTRIP_LIMIT GST_SECOND -/* Minimum timeout will be immediately (ie, as fast as one RTT), but no - * more often than 1/20th second (arbitrarily, to spread observations a little) */ -#define DEFAULT_MINIMUM_UPDATE_INTERVAL (GST_SECOND / 20) -#define DEFAULT_BASE_TIME 0 -#define DEFAULT_QOS_DSCP -1 - -/* Maximum number of clock updates we can skip before updating */ -#define MAX_SKIPPED_UPDATES 5 - -#define MEDIAN_PRE_FILTERING_WINDOW 9 - -enum -{ - PROP_0, - PROP_ADDRESS, - PROP_PORT, - PROP_ROUNDTRIP_LIMIT, - PROP_MINIMUM_UPDATE_INTERVAL, - PROP_BUS, - PROP_BASE_TIME, - PROP_INTERNAL_CLOCK, - PROP_IS_NTP, - PROP_QOS_DSCP -}; - -struct _GstNetClientInternalClock -{ - GstSystemClock clock; - - GThread *thread; - - GSocket *socket; - GSocketAddress *servaddr; - GCancellable *cancel; - gboolean made_cancel_fd; - - GstClockTime timeout_expiration; - GstClockTime roundtrip_limit; - GstClockTime rtt_avg; - GstClockTime minimum_update_interval; - GstClockTime last_remote_poll_interval; - guint skipped_updates; - GstClockTime last_rtts[MEDIAN_PRE_FILTERING_WINDOW]; - gint last_rtts_missing; - - gchar *address; - gint port; - gboolean is_ntp; - gint qos_dscp; - - /* Protected by OBJECT_LOCK */ - GList *busses; -}; - -struct _GstNetClientInternalClockClass -{ - GstSystemClockClass parent_class; -}; - -#define _do_init \ - GST_DEBUG_CATEGORY_INIT (ncc_debug, "netclock", 0, "Network client clock"); - -G_DEFINE_TYPE_WITH_CODE (GstNetClientInternalClock, - gst_net_client_internal_clock, GST_TYPE_SYSTEM_CLOCK, _do_init); - -static void gst_net_client_internal_clock_finalize (GObject * object); -static void gst_net_client_internal_clock_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_net_client_internal_clock_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); -static void gst_net_client_internal_clock_constructed (GObject * object); - -static gboolean gst_net_client_internal_clock_start (GstNetClientInternalClock * - self); -static void gst_net_client_internal_clock_stop (GstNetClientInternalClock * - self); - -static void -gst_net_client_internal_clock_class_init (GstNetClientInternalClockClass * - klass) -{ - GObjectClass *gobject_class; - - gobject_class = G_OBJECT_CLASS (klass); - - gobject_class->finalize = gst_net_client_internal_clock_finalize; - gobject_class->get_property = gst_net_client_internal_clock_get_property; - gobject_class->set_property = gst_net_client_internal_clock_set_property; - gobject_class->constructed = gst_net_client_internal_clock_constructed; - - g_object_class_install_property (gobject_class, PROP_ADDRESS, - g_param_spec_string ("address", "address", - "The IP address of the machine providing a time server", - DEFAULT_ADDRESS, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_PORT, - g_param_spec_int ("port", "port", - "The port on which the remote server is listening", 0, G_MAXUINT16, - DEFAULT_PORT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_IS_NTP, - g_param_spec_boolean ("is-ntp", "Is NTP", - "The clock is using the NTPv4 protocol", FALSE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); -} - -static void -gst_net_client_internal_clock_init (GstNetClientInternalClock * self) -{ - GST_OBJECT_FLAG_SET (self, GST_CLOCK_FLAG_NEEDS_STARTUP_SYNC); - - self->port = DEFAULT_PORT; - self->address = g_strdup (DEFAULT_ADDRESS); - self->is_ntp = FALSE; - self->qos_dscp = DEFAULT_QOS_DSCP; - - gst_clock_set_timeout (GST_CLOCK (self), DEFAULT_TIMEOUT); - - self->thread = NULL; - - self->servaddr = NULL; - self->rtt_avg = GST_CLOCK_TIME_NONE; - self->roundtrip_limit = DEFAULT_ROUNDTRIP_LIMIT; - self->minimum_update_interval = DEFAULT_MINIMUM_UPDATE_INTERVAL; - self->last_remote_poll_interval = GST_CLOCK_TIME_NONE; - self->skipped_updates = 0; - self->last_rtts_missing = MEDIAN_PRE_FILTERING_WINDOW; -} - -static void -gst_net_client_internal_clock_finalize (GObject * object) -{ - GstNetClientInternalClock *self = GST_NET_CLIENT_INTERNAL_CLOCK (object); - - if (self->thread) { - gst_net_client_internal_clock_stop (self); - } - - g_free (self->address); - self->address = NULL; - - if (self->servaddr != NULL) { - g_object_unref (self->servaddr); - self->servaddr = NULL; - } - - if (self->socket != NULL) { - if (!g_socket_close (self->socket, NULL)) - GST_ERROR_OBJECT (self, "Failed to close socket"); - g_object_unref (self->socket); - self->socket = NULL; - } - - g_warn_if_fail (self->busses == NULL); - - G_OBJECT_CLASS (gst_net_client_internal_clock_parent_class)->finalize - (object); -} - -static void -gst_net_client_internal_clock_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstNetClientInternalClock *self = GST_NET_CLIENT_INTERNAL_CLOCK (object); - - switch (prop_id) { - case PROP_ADDRESS: - GST_OBJECT_LOCK (self); - g_free (self->address); - self->address = g_value_dup_string (value); - if (self->address == NULL) - self->address = g_strdup (DEFAULT_ADDRESS); - GST_OBJECT_UNLOCK (self); - break; - case PROP_PORT: - GST_OBJECT_LOCK (self); - self->port = g_value_get_int (value); - GST_OBJECT_UNLOCK (self); - break; - case PROP_IS_NTP: - GST_OBJECT_LOCK (self); - self->is_ntp = g_value_get_boolean (value); - GST_OBJECT_UNLOCK (self); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_net_client_internal_clock_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstNetClientInternalClock *self = GST_NET_CLIENT_INTERNAL_CLOCK (object); - - switch (prop_id) { - case PROP_ADDRESS: - GST_OBJECT_LOCK (self); - g_value_set_string (value, self->address); - GST_OBJECT_UNLOCK (self); - break; - case PROP_PORT: - g_value_set_int (value, self->port); - break; - case PROP_IS_NTP: - g_value_set_boolean (value, self->is_ntp); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_net_client_internal_clock_constructed (GObject * object) -{ - GstNetClientInternalClock *self = GST_NET_CLIENT_INTERNAL_CLOCK (object); - - G_OBJECT_CLASS (gst_net_client_internal_clock_parent_class)->constructed - (object); - - if (!gst_net_client_internal_clock_start (self)) { - g_warning ("failed to start clock '%s'", GST_OBJECT_NAME (self)); - } - - /* all systems go, cap'n */ -} - -static gint -compare_clock_time (const GstClockTime * a, const GstClockTime * b) -{ - if (*a < *b) - return -1; - else if (*a > *b) - return 1; - return 0; -} - -static void -gst_net_client_internal_clock_observe_times (GstNetClientInternalClock * self, - GstClockTime local_1, GstClockTime remote_1, GstClockTime remote_2, - GstClockTime local_2) -{ - GstClockTime current_timeout = 0; - GstClockTime local_avg, remote_avg; - gdouble r_squared; - GstClock *clock; - GstClockTime rtt, rtt_limit, min_update_interval; - /* Use for discont tracking */ - GstClockTime time_before = 0; - GstClockTime min_guess = 0; - GstClockTimeDiff time_discont = 0; - gboolean synched, now_synched; - GstClockTime internal_time, external_time, rate_num, rate_den; - GstClockTime orig_internal_time, orig_external_time, orig_rate_num, - orig_rate_den; - GstClockTime max_discont; - GstClockTime last_rtts[MEDIAN_PRE_FILTERING_WINDOW]; - GstClockTime median; - gint i; - - GST_OBJECT_LOCK (self); - rtt_limit = self->roundtrip_limit; - - GST_LOG_OBJECT (self, - "local1 %" G_GUINT64_FORMAT " remote1 %" G_GUINT64_FORMAT " remote2 %" - G_GUINT64_FORMAT " local2 %" G_GUINT64_FORMAT, local_1, remote_1, - remote_2, local_2); - - /* If the server told us a poll interval and it's bigger than the - * one configured via the property, use the server's */ - if (self->last_remote_poll_interval != GST_CLOCK_TIME_NONE && - self->last_remote_poll_interval > self->minimum_update_interval) - min_update_interval = self->last_remote_poll_interval; - else - min_update_interval = self->minimum_update_interval; - GST_OBJECT_UNLOCK (self); - - if (local_2 < local_1) { - GST_LOG_OBJECT (self, "Dropping observation: receive time %" GST_TIME_FORMAT - " < send time %" GST_TIME_FORMAT, GST_TIME_ARGS (local_1), - GST_TIME_ARGS (local_2)); - goto bogus_observation; - } - - if (remote_2 < remote_1) { - GST_LOG_OBJECT (self, - "Dropping observation: remote receive time %" GST_TIME_FORMAT - " < send time %" GST_TIME_FORMAT, GST_TIME_ARGS (remote_1), - GST_TIME_ARGS (remote_2)); - goto bogus_observation; - } - - /* The round trip time is (assuming symmetric path delays) - * delta = (local_2 - local_1) - (remote_2 - remote_1) - */ - - rtt = GST_CLOCK_DIFF (local_1, local_2) - GST_CLOCK_DIFF (remote_1, remote_2); - - if ((rtt_limit > 0) && (rtt > rtt_limit)) { - GST_LOG_OBJECT (self, - "Dropping observation: RTT %" GST_TIME_FORMAT " > limit %" - GST_TIME_FORMAT, GST_TIME_ARGS (rtt), GST_TIME_ARGS (rtt_limit)); - goto bogus_observation; - } - - for (i = 1; i < MEDIAN_PRE_FILTERING_WINDOW; i++) - self->last_rtts[i - 1] = self->last_rtts[i]; - self->last_rtts[i - 1] = rtt; - - if (self->last_rtts_missing) { - self->last_rtts_missing--; - } else { - memcpy (&last_rtts, &self->last_rtts, sizeof (last_rtts)); - g_qsort_with_data (&last_rtts, - MEDIAN_PRE_FILTERING_WINDOW, sizeof (GstClockTime), - (GCompareDataFunc) compare_clock_time, NULL); - - median = last_rtts[MEDIAN_PRE_FILTERING_WINDOW / 2]; - - /* FIXME: We might want to use something else here, like only allowing - * things in the interquartile range, or also filtering away delays that - * are too small compared to the median. This here worked well enough - * in tests so far. - */ - if (rtt > 2 * median) { - GST_LOG_OBJECT (self, - "Dropping observation, long RTT %" GST_TIME_FORMAT " > 2 * median %" - GST_TIME_FORMAT, GST_TIME_ARGS (rtt), GST_TIME_ARGS (median)); - goto bogus_observation; - } - } - - /* Track an average round trip time, for a bit of smoothing */ - /* Always update before discarding a sample, so genuine changes in - * the network get picked up, eventually */ - if (self->rtt_avg == GST_CLOCK_TIME_NONE) - self->rtt_avg = rtt; - else if (rtt < self->rtt_avg) /* Shorter RTTs carry more weight than longer */ - self->rtt_avg = (3 * self->rtt_avg + rtt) / 4; - else - self->rtt_avg = (15 * self->rtt_avg + rtt) / 16; - - if (rtt > 2 * self->rtt_avg) { - GST_LOG_OBJECT (self, - "Dropping observation, long RTT %" GST_TIME_FORMAT " > 2 * avg %" - GST_TIME_FORMAT, GST_TIME_ARGS (rtt), GST_TIME_ARGS (self->rtt_avg)); - goto bogus_observation; - } - - /* The difference between the local and remote clock (again assuming - * symmetric path delays): - * - * local_1 + delta / 2 - remote_1 = theta - * or - * local_2 - delta / 2 - remote_2 = theta - * - * which gives after some simple algebraic transformations: - * - * (remote_1 - local_1) + (remote_2 - local_2) - * theta = ------------------------------------------- - * 2 - * - * - * Thus remote time at local_avg is equal to: - * - * local_avg + theta = - * - * local_1 + local_2 (remote_1 - local_1) + (remote_2 - local_2) - * ----------------- + ------------------------------------------- - * 2 2 - * - * = - * - * remote_1 + remote_2 - * ------------------- = remote_avg - * 2 - * - * We use this for our clock estimation, i.e. local_avg at remote clock - * being the same as remote_avg. - */ - - local_avg = (local_2 + local_1) / 2; - remote_avg = (remote_2 + remote_1) / 2; - - GST_LOG_OBJECT (self, - "remoteavg %" G_GUINT64_FORMAT " localavg %" G_GUINT64_FORMAT, - remote_avg, local_avg); - - clock = GST_CLOCK_CAST (self); - - /* Store what the clock produced as 'now' before this update */ - gst_clock_get_calibration (GST_CLOCK_CAST (self), &orig_internal_time, - &orig_external_time, &orig_rate_num, &orig_rate_den); - internal_time = orig_internal_time; - external_time = orig_external_time; - rate_num = orig_rate_num; - rate_den = orig_rate_den; - - min_guess = - gst_clock_adjust_with_calibration (GST_CLOCK_CAST (self), local_1, - internal_time, external_time, rate_num, rate_den); - time_before = - gst_clock_adjust_with_calibration (GST_CLOCK_CAST (self), local_2, - internal_time, external_time, rate_num, rate_den); - - /* Maximum discontinuity, when we're synched with the master. Could make this a property, - * but this value seems to work fine */ - max_discont = self->rtt_avg / 4; - - /* If the remote observation was within a max_discont window around our min/max estimates, we're synched */ - synched = - (GST_CLOCK_DIFF (remote_avg, min_guess) < (GstClockTimeDiff) (max_discont) - && GST_CLOCK_DIFF (time_before, - remote_avg) < (GstClockTimeDiff) (max_discont)); - - if (gst_clock_add_observation_unapplied (GST_CLOCK_CAST (self), - local_avg, remote_avg, &r_squared, &internal_time, &external_time, - &rate_num, &rate_den)) { - - /* Now compare the difference (discont) in the clock - * after this observation */ - time_discont = GST_CLOCK_DIFF (time_before, - gst_clock_adjust_with_calibration (GST_CLOCK_CAST (self), local_2, - internal_time, external_time, rate_num, rate_den)); - - /* If we were in sync with the remote clock, clamp the allowed - * discontinuity to within quarter of one RTT. In sync means our send/receive estimates - * of remote time correctly windowed the actual remote time observation */ - if (synched && ABS (time_discont) > max_discont) { - GstClockTimeDiff offset; - GST_DEBUG_OBJECT (clock, - "Too large a discont, clamping to 1/4 average RTT = %" - GST_TIME_FORMAT, GST_TIME_ARGS (max_discont)); - if (time_discont > 0) { /* Too large a forward step - add a -ve offset */ - offset = max_discont - time_discont; - if (-offset > external_time) - external_time = 0; - else - external_time += offset; - } else { /* Too large a backward step - add a +ve offset */ - offset = -(max_discont + time_discont); - external_time += offset; - } - - time_discont += offset; - } - - /* Check if the new clock params would have made our observation within range */ - now_synched = - (GST_CLOCK_DIFF (remote_avg, - gst_clock_adjust_with_calibration (GST_CLOCK_CAST (self), - local_1, internal_time, external_time, rate_num, - rate_den)) < (GstClockTimeDiff) (max_discont)) - && - (GST_CLOCK_DIFF (gst_clock_adjust_with_calibration - (GST_CLOCK_CAST (self), local_2, internal_time, external_time, - rate_num, rate_den), - remote_avg) < (GstClockTimeDiff) (max_discont)); - - /* Only update the clock if we had synch or just gained it */ - if (synched || now_synched || self->skipped_updates > MAX_SKIPPED_UPDATES) { - gst_clock_set_calibration (GST_CLOCK_CAST (self), internal_time, - external_time, rate_num, rate_den); - /* ghetto formula - shorter timeout for bad correlations */ - current_timeout = (1e-3 / (1 - MIN (r_squared, 0.99999))) * GST_SECOND; - current_timeout = - MIN (current_timeout, gst_clock_get_timeout (GST_CLOCK_CAST (self))); - self->skipped_updates = 0; - - /* FIXME: When do we consider the clock absolutely not synced anymore? */ - gst_clock_set_synced (GST_CLOCK (self), TRUE); - } else { - /* Restore original calibration vars for the report, we're not changing the clock */ - internal_time = orig_internal_time; - external_time = orig_external_time; - rate_num = orig_rate_num; - rate_den = orig_rate_den; - time_discont = 0; - self->skipped_updates++; - } - } - - /* Limit the polling to at most one per minimum_update_interval */ - if (rtt < min_update_interval) - current_timeout = MAX (min_update_interval - rtt, current_timeout); - - GST_OBJECT_LOCK (self); - if (self->busses) { - GstStructure *s; - GstMessage *msg; - GList *l; - - /* Output a stats message, whether we updated the clock or not */ - s = gst_structure_new ("gst-netclock-statistics", - "synchronised", G_TYPE_BOOLEAN, synched, - "rtt", G_TYPE_UINT64, rtt, - "rtt-average", G_TYPE_UINT64, self->rtt_avg, - "local", G_TYPE_UINT64, local_avg, - "remote", G_TYPE_UINT64, remote_avg, - "discontinuity", G_TYPE_INT64, time_discont, - "remote-min-estimate", G_TYPE_UINT64, min_guess, - "remote-max-estimate", G_TYPE_UINT64, time_before, - "remote-min-error", G_TYPE_INT64, GST_CLOCK_DIFF (remote_avg, - min_guess), "remote-max-error", G_TYPE_INT64, - GST_CLOCK_DIFF (remote_avg, time_before), "request-send", G_TYPE_UINT64, - local_1, "request-receive", G_TYPE_UINT64, local_2, "r-squared", - G_TYPE_DOUBLE, r_squared, "timeout", G_TYPE_UINT64, current_timeout, - "internal-time", G_TYPE_UINT64, internal_time, "external-time", - G_TYPE_UINT64, external_time, "rate-num", G_TYPE_UINT64, rate_num, - "rate-den", G_TYPE_UINT64, rate_den, "rate", G_TYPE_DOUBLE, - (gdouble) (rate_num) / rate_den, "local-clock-offset", G_TYPE_INT64, - GST_CLOCK_DIFF (internal_time, external_time), NULL); - msg = gst_message_new_element (GST_OBJECT (self), s); - - for (l = self->busses; l; l = l->next) - gst_bus_post (l->data, gst_message_ref (msg)); - gst_message_unref (msg); - } - GST_OBJECT_UNLOCK (self); - - GST_INFO ("next timeout: %" GST_TIME_FORMAT, GST_TIME_ARGS (current_timeout)); - self->timeout_expiration = gst_util_get_timestamp () + current_timeout; - - return; - -bogus_observation: - /* Schedule a new packet again soon */ - self->timeout_expiration = gst_util_get_timestamp () + (GST_SECOND / 4); - return; -} - -static gpointer -gst_net_client_internal_clock_thread (gpointer data) -{ - GstNetClientInternalClock *self = data; - GSocket *socket = self->socket; - GError *err = NULL; - gint cur_qos_dscp = DEFAULT_QOS_DSCP; - - GST_INFO_OBJECT (self, "net client clock thread running, socket=%p", socket); - - g_socket_set_blocking (socket, TRUE); - g_socket_set_timeout (socket, 0); - - while (!g_cancellable_is_cancelled (self->cancel)) { - GstClockTime expiration_time = self->timeout_expiration; - GstClockTime now = gst_util_get_timestamp (); - gint64 socket_timeout; - - if (now >= expiration_time || (expiration_time - now) <= GST_MSECOND) { - socket_timeout = 0; - } else { - socket_timeout = (expiration_time - now) / GST_USECOND; - } - - GST_TRACE_OBJECT (self, "timeout: %" G_GINT64_FORMAT "us", socket_timeout); - - if (!g_socket_condition_timed_wait (socket, G_IO_IN, socket_timeout, - self->cancel, &err)) { - /* cancelled, timeout or error */ - if (err->code == G_IO_ERROR_CANCELLED) { - GST_INFO_OBJECT (self, "cancelled"); - g_clear_error (&err); - break; - } else if (err->code == G_IO_ERROR_TIMED_OUT) { - gint new_qos_dscp; - - /* timed out, let's send another packet */ - GST_DEBUG_OBJECT (self, "timed out"); - - /* before next sending check if need to change QoS */ - new_qos_dscp = self->qos_dscp; - if (cur_qos_dscp != new_qos_dscp && - gst_net_utils_set_socket_tos (socket, new_qos_dscp)) { - GST_DEBUG_OBJECT (self, "changed QoS DSCP to: %d", new_qos_dscp); - cur_qos_dscp = new_qos_dscp; - } - - if (self->is_ntp) { - GstNtpPacket *packet; - - packet = gst_ntp_packet_new (NULL, NULL); - - packet->transmit_time = - gst_clock_get_internal_time (GST_CLOCK_CAST (self)); - - GST_DEBUG_OBJECT (self, - "sending packet, local time = %" GST_TIME_FORMAT, - GST_TIME_ARGS (packet->transmit_time)); - - gst_ntp_packet_send (packet, self->socket, self->servaddr, NULL); - - g_free (packet); - } else { - GstNetTimePacket *packet; - - packet = gst_net_time_packet_new (NULL); - - packet->local_time = - gst_clock_get_internal_time (GST_CLOCK_CAST (self)); - - GST_DEBUG_OBJECT (self, - "sending packet, local time = %" GST_TIME_FORMAT, - GST_TIME_ARGS (packet->local_time)); - - gst_net_time_packet_send (packet, self->socket, self->servaddr, NULL); - - g_free (packet); - } - - /* reset timeout (but are expecting a response sooner anyway) */ - self->timeout_expiration = - gst_util_get_timestamp () + - gst_clock_get_timeout (GST_CLOCK_CAST (self)); - } else { - GST_DEBUG_OBJECT (self, "socket error: %s", err->message); - g_usleep (G_USEC_PER_SEC / 10); /* throttle */ - } - g_clear_error (&err); - } else { - GstClockTime new_local; - - /* got packet */ - - new_local = gst_clock_get_internal_time (GST_CLOCK_CAST (self)); - - if (self->is_ntp) { - GstNtpPacket *packet; - - packet = gst_ntp_packet_receive (socket, NULL, &err); - - if (packet != NULL) { - GST_LOG_OBJECT (self, "got packet back"); - GST_LOG_OBJECT (self, "local_1 = %" GST_TIME_FORMAT, - GST_TIME_ARGS (packet->origin_time)); - GST_LOG_OBJECT (self, "remote_1 = %" GST_TIME_FORMAT, - GST_TIME_ARGS (packet->receive_time)); - GST_LOG_OBJECT (self, "remote_2 = %" GST_TIME_FORMAT, - GST_TIME_ARGS (packet->transmit_time)); - GST_LOG_OBJECT (self, "local_2 = %" GST_TIME_FORMAT, - GST_TIME_ARGS (new_local)); - GST_LOG_OBJECT (self, "poll_interval = %" GST_TIME_FORMAT, - GST_TIME_ARGS (packet->poll_interval)); - - /* Remember the last poll interval we ever got from the server */ - if (packet->poll_interval != GST_CLOCK_TIME_NONE) - self->last_remote_poll_interval = packet->poll_interval; - - /* observe_times will reset the timeout */ - gst_net_client_internal_clock_observe_times (self, - packet->origin_time, packet->receive_time, packet->transmit_time, - new_local); - - g_free (packet); - } else if (err != NULL) { - if (g_error_matches (err, GST_NTP_ERROR, GST_NTP_ERROR_WRONG_VERSION) - || g_error_matches (err, GST_NTP_ERROR, GST_NTP_ERROR_KOD_DENY)) { - GST_ERROR_OBJECT (self, "fatal receive error: %s", err->message); - g_clear_error (&err); - break; - } else if (g_error_matches (err, GST_NTP_ERROR, - GST_NTP_ERROR_KOD_RATE)) { - GST_WARNING_OBJECT (self, "need to limit rate"); - - /* If the server did not tell us a poll interval before, double - * our minimum poll interval. Otherwise we assume that the server - * already told us something sensible and that this error here - * was just a spurious error */ - if (self->last_remote_poll_interval == GST_CLOCK_TIME_NONE) - self->minimum_update_interval *= 2; - - /* And wait a bit before we send the next packet instead of - * sending it immediately */ - self->timeout_expiration = - gst_util_get_timestamp () + - gst_clock_get_timeout (GST_CLOCK_CAST (self)); - } else { - GST_WARNING_OBJECT (self, "receive error: %s", err->message); - } - g_clear_error (&err); - } - } else { - GstNetTimePacket *packet; - - packet = gst_net_time_packet_receive (socket, NULL, &err); - - if (packet != NULL) { - GST_LOG_OBJECT (self, "got packet back"); - GST_LOG_OBJECT (self, "local_1 = %" GST_TIME_FORMAT, - GST_TIME_ARGS (packet->local_time)); - GST_LOG_OBJECT (self, "remote = %" GST_TIME_FORMAT, - GST_TIME_ARGS (packet->remote_time)); - GST_LOG_OBJECT (self, "local_2 = %" GST_TIME_FORMAT, - GST_TIME_ARGS (new_local)); - - /* observe_times will reset the timeout */ - gst_net_client_internal_clock_observe_times (self, packet->local_time, - packet->remote_time, packet->remote_time, new_local); - - g_free (packet); - } else if (err != NULL) { - GST_WARNING_OBJECT (self, "receive error: %s", err->message); - g_clear_error (&err); - } - } - } - } - GST_INFO_OBJECT (self, "shutting down net client clock thread"); - return NULL; -} - -static gboolean -gst_net_client_internal_clock_start (GstNetClientInternalClock * self) -{ - GSocketAddress *servaddr; - GSocketAddress *myaddr; - GSocketAddress *anyaddr; - GInetAddress *inetaddr; - GSocket *socket; - GError *error = NULL; - GSocketFamily family; - GPollFD dummy_pollfd; - GResolver *resolver = NULL; - GError *err = NULL; - - g_return_val_if_fail (self->address != NULL, FALSE); - g_return_val_if_fail (self->servaddr == NULL, FALSE); - - /* create target address */ - inetaddr = g_inet_address_new_from_string (self->address); - if (inetaddr == NULL) { - GList *results; - - resolver = g_resolver_get_default (); - - results = g_resolver_lookup_by_name (resolver, self->address, NULL, &err); - if (!results) - goto failed_to_resolve; - - inetaddr = G_INET_ADDRESS (g_object_ref (results->data)); - g_resolver_free_addresses (results); - g_object_unref (resolver); - } - - family = g_inet_address_get_family (inetaddr); - - servaddr = g_inet_socket_address_new (inetaddr, self->port); - g_object_unref (inetaddr); - - g_assert (servaddr != NULL); - - GST_DEBUG_OBJECT (self, "will communicate with %s:%d", self->address, - self->port); - - socket = g_socket_new (family, G_SOCKET_TYPE_DATAGRAM, - G_SOCKET_PROTOCOL_UDP, &error); - - if (socket == NULL) - goto no_socket; - - GST_DEBUG_OBJECT (self, "binding socket"); - inetaddr = g_inet_address_new_any (family); - anyaddr = g_inet_socket_address_new (inetaddr, 0); - g_socket_bind (socket, anyaddr, TRUE, &error); - g_object_unref (anyaddr); - g_object_unref (inetaddr); - - if (error != NULL) - goto bind_error; - - /* check address we're bound to, mostly for debugging purposes */ - myaddr = g_socket_get_local_address (socket, &error); - - if (myaddr == NULL) - goto getsockname_error; - - GST_DEBUG_OBJECT (self, "socket opened on UDP port %d", - g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (myaddr))); - - g_object_unref (myaddr); - - self->cancel = g_cancellable_new (); - self->made_cancel_fd = - g_cancellable_make_pollfd (self->cancel, &dummy_pollfd); - - self->socket = socket; - self->servaddr = G_SOCKET_ADDRESS (servaddr); - - self->thread = g_thread_try_new ("GstNetClientInternalClock", - gst_net_client_internal_clock_thread, self, &error); - - if (error != NULL) - goto no_thread; - - return TRUE; - - /* ERRORS */ -no_socket: - { - GST_ERROR_OBJECT (self, "socket_new() failed: %s", error->message); - g_error_free (error); - return FALSE; - } -bind_error: - { - GST_ERROR_OBJECT (self, "bind failed: %s", error->message); - g_error_free (error); - g_object_unref (socket); - return FALSE; - } -getsockname_error: - { - GST_ERROR_OBJECT (self, "get_local_address() failed: %s", error->message); - g_error_free (error); - g_object_unref (socket); - return FALSE; - } -failed_to_resolve: - { - GST_ERROR_OBJECT (self, "resolving '%s' failed: %s", - self->address, err->message); - g_clear_error (&err); - g_object_unref (resolver); - return FALSE; - } -no_thread: - { - GST_ERROR_OBJECT (self, "could not create thread: %s", error->message); - g_object_unref (self->servaddr); - self->servaddr = NULL; - g_object_unref (self->socket); - self->socket = NULL; - g_error_free (error); - return FALSE; - } -} - -static void -gst_net_client_internal_clock_stop (GstNetClientInternalClock * self) -{ - if (self->thread == NULL) - return; - - GST_INFO_OBJECT (self, "stopping..."); - g_cancellable_cancel (self->cancel); - - g_thread_join (self->thread); - self->thread = NULL; - - if (self->made_cancel_fd) - g_cancellable_release_fd (self->cancel); - - g_object_unref (self->cancel); - self->cancel = NULL; - - g_object_unref (self->servaddr); - self->servaddr = NULL; - - g_object_unref (self->socket); - self->socket = NULL; - - GST_INFO_OBJECT (self, "stopped"); -} - -struct _GstNetClientClockPrivate -{ - GstClock *internal_clock; - - GstClockTime roundtrip_limit; - GstClockTime minimum_update_interval; - - GstClockTime base_time, internal_base_time; - - gchar *address; - gint port; - gint qos_dscp; - - GstBus *bus; - - gboolean is_ntp; - - gulong synced_id; -}; - -G_DEFINE_TYPE_WITH_PRIVATE (GstNetClientClock, gst_net_client_clock, - GST_TYPE_SYSTEM_CLOCK); - -static void gst_net_client_clock_finalize (GObject * object); -static void gst_net_client_clock_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_net_client_clock_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static void gst_net_client_clock_constructed (GObject * object); - -static GstClockTime gst_net_client_clock_get_internal_time (GstClock * clock); - -static void -gst_net_client_clock_class_init (GstNetClientClockClass * klass) -{ - GObjectClass *gobject_class; - GstClockClass *clock_class; - - gobject_class = G_OBJECT_CLASS (klass); - clock_class = GST_CLOCK_CLASS (klass); - - gobject_class->finalize = gst_net_client_clock_finalize; - gobject_class->get_property = gst_net_client_clock_get_property; - gobject_class->set_property = gst_net_client_clock_set_property; - gobject_class->constructed = gst_net_client_clock_constructed; - - g_object_class_install_property (gobject_class, PROP_ADDRESS, - g_param_spec_string ("address", "address", - "The IP address of the machine providing a time server", - DEFAULT_ADDRESS, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_PORT, - g_param_spec_int ("port", "port", - "The port on which the remote server is listening", 0, G_MAXUINT16, - DEFAULT_PORT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_BUS, - g_param_spec_object ("bus", "bus", - "A GstBus on which to send clock status information", GST_TYPE_BUS, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - /** - * GstNetClientInternalClock::round-trip-limit: - * - * Maximum allowed round-trip for packets. If this property is set to a nonzero - * value, all packets with a round-trip interval larger than this limit will be - * ignored. This is useful for networks with severe and fluctuating transport - * delays. Filtering out these packets increases stability of the synchronization. - * On the other hand, the lower the limit, the higher the amount of filtered - * packets. Empirical tests are typically necessary to estimate a good value - * for the limit. - * If the property is set to zero, the limit is disabled. - * - * Since: 1.4 - */ - g_object_class_install_property (gobject_class, PROP_ROUNDTRIP_LIMIT, - g_param_spec_uint64 ("round-trip-limit", "round-trip limit", - "Maximum tolerable round-trip interval for packets, in nanoseconds " - "(0 = no limit)", 0, G_MAXUINT64, DEFAULT_ROUNDTRIP_LIMIT, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_MINIMUM_UPDATE_INTERVAL, - g_param_spec_uint64 ("minimum-update-interval", "minimum update interval", - "Minimum polling interval for packets, in nanoseconds" - "(0 = no limit)", 0, G_MAXUINT64, DEFAULT_MINIMUM_UPDATE_INTERVAL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_BASE_TIME, - g_param_spec_uint64 ("base-time", "Base Time", - "Initial time that is reported before synchronization", 0, - G_MAXUINT64, DEFAULT_BASE_TIME, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_INTERNAL_CLOCK, - g_param_spec_object ("internal-clock", "Internal Clock", - "Internal clock that directly slaved to the remote clock", - GST_TYPE_CLOCK, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_QOS_DSCP, - g_param_spec_int ("qos-dscp", "QoS diff srv code point", - "Quality of Service, differentiated services code point (-1 default)", - -1, 63, DEFAULT_QOS_DSCP, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - clock_class->get_internal_time = gst_net_client_clock_get_internal_time; -} - -static void -gst_net_client_clock_init (GstNetClientClock * self) -{ - GstNetClientClockPrivate *priv; - GstClock *clock; - - self->priv = priv = gst_net_client_clock_get_instance_private (self); - - GST_OBJECT_FLAG_SET (self, GST_CLOCK_FLAG_CAN_SET_MASTER); - GST_OBJECT_FLAG_SET (self, GST_CLOCK_FLAG_NEEDS_STARTUP_SYNC); - - priv->port = DEFAULT_PORT; - priv->address = g_strdup (DEFAULT_ADDRESS); - priv->qos_dscp = DEFAULT_QOS_DSCP; - - priv->roundtrip_limit = DEFAULT_ROUNDTRIP_LIMIT; - priv->minimum_update_interval = DEFAULT_MINIMUM_UPDATE_INTERVAL; - - clock = gst_system_clock_obtain (); - priv->base_time = DEFAULT_BASE_TIME; - priv->internal_base_time = gst_clock_get_time (clock); - gst_object_unref (clock); -} - -/* Must be called with clocks_lock */ -static void -update_clock_cache (ClockCache * cache) -{ - GstClockTime roundtrip_limit = 0, minimum_update_interval = 0; - GList *l, *busses = NULL; - gint qos_dscp = DEFAULT_QOS_DSCP; - - GST_OBJECT_LOCK (cache->clock); - g_list_free_full (GST_NET_CLIENT_INTERNAL_CLOCK (cache->clock)->busses, - (GDestroyNotify) gst_object_unref); - - for (l = cache->clocks; l; l = l->next) { - GstNetClientClock *clock = l->data; - - if (clock->priv->bus) - busses = g_list_prepend (busses, gst_object_ref (clock->priv->bus)); - - if (roundtrip_limit == 0) - roundtrip_limit = clock->priv->roundtrip_limit; - else - roundtrip_limit = MAX (roundtrip_limit, clock->priv->roundtrip_limit); - - if (minimum_update_interval == 0) - minimum_update_interval = clock->priv->minimum_update_interval; - else - minimum_update_interval = - MIN (minimum_update_interval, clock->priv->minimum_update_interval); - - qos_dscp = MAX (qos_dscp, clock->priv->qos_dscp); - } - GST_NET_CLIENT_INTERNAL_CLOCK (cache->clock)->busses = busses; - GST_NET_CLIENT_INTERNAL_CLOCK (cache->clock)->roundtrip_limit = - roundtrip_limit; - GST_NET_CLIENT_INTERNAL_CLOCK (cache->clock)->minimum_update_interval = - minimum_update_interval; - GST_NET_CLIENT_INTERNAL_CLOCK (cache->clock)->qos_dscp = qos_dscp; - - GST_OBJECT_UNLOCK (cache->clock); -} - -static gboolean -remove_clock_cache (GstClock * clock, GstClockTime time, GstClockID id, - gpointer user_data) -{ - ClockCache *cache = user_data; - - G_LOCK (clocks_lock); - if (!cache->clocks) { - gst_clock_id_unref (cache->remove_id); - gst_object_unref (cache->clock); - clocks = g_list_remove (clocks, cache); - g_free (cache); - } - G_UNLOCK (clocks_lock); - - return TRUE; -} - -static void -gst_net_client_clock_finalize (GObject * object) -{ - GstNetClientClock *self = GST_NET_CLIENT_CLOCK (object); - GList *l; - - if (self->priv->synced_id) - g_signal_handler_disconnect (self->priv->internal_clock, - self->priv->synced_id); - self->priv->synced_id = 0; - - G_LOCK (clocks_lock); - for (l = clocks; l; l = l->next) { - ClockCache *cache = l->data; - - if (cache->clock == self->priv->internal_clock) { - cache->clocks = g_list_remove (cache->clocks, self); - - if (cache->clocks) { - update_clock_cache (cache); - } else { - GstClock *sysclock = gst_system_clock_obtain (); - GstClockTime time = gst_clock_get_time (sysclock) + 60 * GST_SECOND; - - cache->remove_id = gst_clock_new_single_shot_id (sysclock, time); - gst_clock_id_wait_async (cache->remove_id, remove_clock_cache, cache, - NULL); - gst_object_unref (sysclock); - } - break; - } - } - G_UNLOCK (clocks_lock); - - g_free (self->priv->address); - self->priv->address = NULL; - - if (self->priv->bus != NULL) { - gst_object_unref (self->priv->bus); - self->priv->bus = NULL; - } - - G_OBJECT_CLASS (gst_net_client_clock_parent_class)->finalize (object); -} - -static void -gst_net_client_clock_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstNetClientClock *self = GST_NET_CLIENT_CLOCK (object); - gboolean update = FALSE; - - switch (prop_id) { - case PROP_ADDRESS: - GST_OBJECT_LOCK (self); - g_free (self->priv->address); - self->priv->address = g_value_dup_string (value); - if (self->priv->address == NULL) - self->priv->address = g_strdup (DEFAULT_ADDRESS); - GST_OBJECT_UNLOCK (self); - break; - case PROP_PORT: - GST_OBJECT_LOCK (self); - self->priv->port = g_value_get_int (value); - GST_OBJECT_UNLOCK (self); - break; - case PROP_ROUNDTRIP_LIMIT: - GST_OBJECT_LOCK (self); - self->priv->roundtrip_limit = g_value_get_uint64 (value); - GST_OBJECT_UNLOCK (self); - update = TRUE; - break; - case PROP_MINIMUM_UPDATE_INTERVAL: - GST_OBJECT_LOCK (self); - self->priv->minimum_update_interval = g_value_get_uint64 (value); - GST_OBJECT_UNLOCK (self); - update = TRUE; - break; - case PROP_BUS: - GST_OBJECT_LOCK (self); - if (self->priv->bus) - gst_object_unref (self->priv->bus); - self->priv->bus = g_value_dup_object (value); - GST_OBJECT_UNLOCK (self); - update = TRUE; - break; - case PROP_BASE_TIME:{ - GstClock *clock; - - self->priv->base_time = g_value_get_uint64 (value); - clock = gst_system_clock_obtain (); - self->priv->internal_base_time = gst_clock_get_time (clock); - gst_object_unref (clock); - break; - } - case PROP_QOS_DSCP: - GST_OBJECT_LOCK (self); - self->priv->qos_dscp = g_value_get_int (value); - GST_OBJECT_UNLOCK (self); - update = TRUE; - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - - if (update && self->priv->internal_clock) { - GList *l; - - G_LOCK (clocks_lock); - for (l = clocks; l; l = l->next) { - ClockCache *cache = l->data; - - if (cache->clock == self->priv->internal_clock) { - update_clock_cache (cache); - } - } - G_UNLOCK (clocks_lock); - } -} - -static void -gst_net_client_clock_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstNetClientClock *self = GST_NET_CLIENT_CLOCK (object); - - switch (prop_id) { - case PROP_ADDRESS: - GST_OBJECT_LOCK (self); - g_value_set_string (value, self->priv->address); - GST_OBJECT_UNLOCK (self); - break; - case PROP_PORT: - g_value_set_int (value, self->priv->port); - break; - case PROP_ROUNDTRIP_LIMIT: - GST_OBJECT_LOCK (self); - g_value_set_uint64 (value, self->priv->roundtrip_limit); - GST_OBJECT_UNLOCK (self); - break; - case PROP_MINIMUM_UPDATE_INTERVAL: - GST_OBJECT_LOCK (self); - g_value_set_uint64 (value, self->priv->minimum_update_interval); - GST_OBJECT_UNLOCK (self); - break; - case PROP_BUS: - GST_OBJECT_LOCK (self); - g_value_set_object (value, self->priv->bus); - GST_OBJECT_UNLOCK (self); - break; - case PROP_BASE_TIME: - g_value_set_uint64 (value, self->priv->base_time); - break; - case PROP_INTERNAL_CLOCK: - g_value_set_object (value, self->priv->internal_clock); - break; - case PROP_QOS_DSCP: - GST_OBJECT_LOCK (self); - g_value_set_int (value, self->priv->qos_dscp); - GST_OBJECT_UNLOCK (self); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_net_client_clock_synced_cb (GstClock * internal_clock, gboolean synced, - GstClock * self) -{ - gst_clock_set_synced (self, synced); -} - -static void -gst_net_client_clock_constructed (GObject * object) -{ - GstNetClientClock *self = GST_NET_CLIENT_CLOCK (object); - GstClock *internal_clock; - GList *l; - ClockCache *cache = NULL; - - G_OBJECT_CLASS (gst_net_client_clock_parent_class)->constructed (object); - - G_LOCK (clocks_lock); - for (l = clocks; l; l = l->next) { - ClockCache *tmp = l->data; - GstNetClientInternalClock *internal_clock = - GST_NET_CLIENT_INTERNAL_CLOCK (tmp->clock); - - if (strcmp (internal_clock->address, self->priv->address) == 0 && - internal_clock->port == self->priv->port) { - cache = tmp; - - if (cache->remove_id) { - gst_clock_id_unschedule (cache->remove_id); - cache->remove_id = NULL; - } - break; - } - } - - if (!cache) { - cache = g_new0 (ClockCache, 1); - - cache->clock = - g_object_new (GST_TYPE_NET_CLIENT_INTERNAL_CLOCK, "address", - self->priv->address, "port", self->priv->port, "is-ntp", - self->priv->is_ntp, NULL); - gst_object_ref_sink (cache->clock); - clocks = g_list_prepend (clocks, cache); - - /* Not actually leaked but is cached for a while before being disposed, - * see gst_net_client_clock_finalize, so pretend it is to not confuse - * tests. */ - GST_OBJECT_FLAG_SET (cache->clock, GST_OBJECT_FLAG_MAY_BE_LEAKED); - } - - cache->clocks = g_list_prepend (cache->clocks, self); - - GST_OBJECT_LOCK (cache->clock); - if (gst_clock_is_synced (cache->clock)) - gst_clock_set_synced (GST_CLOCK (self), TRUE); - self->priv->synced_id = - g_signal_connect (cache->clock, "synced", - G_CALLBACK (gst_net_client_clock_synced_cb), self); - GST_OBJECT_UNLOCK (cache->clock); - - G_UNLOCK (clocks_lock); - - self->priv->internal_clock = internal_clock = cache->clock; - - /* all systems go, cap'n */ -} - -static GstClockTime -gst_net_client_clock_get_internal_time (GstClock * clock) -{ - GstNetClientClock *self = GST_NET_CLIENT_CLOCK (clock); - - if (!gst_clock_is_synced (self->priv->internal_clock)) { - GstClockTime now = gst_clock_get_internal_time (self->priv->internal_clock); - return gst_clock_adjust_with_calibration (self->priv->internal_clock, now, - self->priv->internal_base_time, self->priv->base_time, 1, 1); - } - - return gst_clock_get_time (self->priv->internal_clock); -} - -/** - * gst_net_client_clock_new: - * @name: a name for the clock - * @remote_address: the address or hostname of the remote clock provider - * @remote_port: the port of the remote clock provider - * @base_time: initial time of the clock - * - * Create a new #GstNetClientClock that will report the time - * provided by the #GstNetTimeProvider on @remote_address and - * @remote_port. - * - * Returns: (transfer full): a new #GstClock that receives a time from the remote - * clock. - */ -GstClock * -gst_net_client_clock_new (const gchar * name, const gchar * remote_address, - gint remote_port, GstClockTime base_time) -{ - GstClock *ret; - - g_return_val_if_fail (remote_address != NULL, NULL); - g_return_val_if_fail (remote_port > 0, NULL); - g_return_val_if_fail (remote_port <= G_MAXUINT16, NULL); - g_return_val_if_fail (base_time != GST_CLOCK_TIME_NONE, NULL); - - ret = - g_object_new (GST_TYPE_NET_CLIENT_CLOCK, "name", name, "address", - remote_address, "port", remote_port, "base-time", base_time, NULL); - - /* Clear floating flag */ - gst_object_ref_sink (ret); - - return ret; -} - -G_DEFINE_TYPE (GstNtpClock, gst_ntp_clock, GST_TYPE_NET_CLIENT_CLOCK); - -static void -gst_ntp_clock_class_init (GstNtpClockClass * klass) -{ -} - -static void -gst_ntp_clock_init (GstNtpClock * self) -{ - GST_NET_CLIENT_CLOCK (self)->priv->is_ntp = TRUE; -} - -/** - * gst_ntp_clock_new: - * @name: a name for the clock - * @remote_address: the address or hostname of the remote clock provider - * @remote_port: the port of the remote clock provider - * @base_time: initial time of the clock - * - * Create a new #GstNtpClock that will report the time provided by - * the NTPv4 server on @remote_address and @remote_port. - * - * Returns: (transfer full): a new #GstClock that receives a time from the remote - * clock. - * - * Since: 1.6 - */ -GstClock * -gst_ntp_clock_new (const gchar * name, const gchar * remote_address, - gint remote_port, GstClockTime base_time) -{ - GstClock *ret; - - g_return_val_if_fail (remote_address != NULL, NULL); - g_return_val_if_fail (remote_port > 0, NULL); - g_return_val_if_fail (remote_port <= G_MAXUINT16, NULL); - g_return_val_if_fail (base_time != GST_CLOCK_TIME_NONE, NULL); - - ret = - g_object_new (GST_TYPE_NTP_CLOCK, "name", name, "address", remote_address, - "port", remote_port, "base-time", base_time, NULL); - - gst_object_ref_sink (ret); - - return ret; -} diff --git a/libs/gst/net/gstnetclientclock.h b/libs/gst/net/gstnetclientclock.h deleted file mode 100644 index 506270c7d5..0000000000 --- a/libs/gst/net/gstnetclientclock.h +++ /dev/null @@ -1,106 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu> - * 2005 Wim Taymans <wim@fluendo.com> - * 2005 Andy Wingo <wingo@pobox.com> - * Copyright (C) 2012 Collabora Ltd. <tim.muller@collabora.co.uk> - * - * gstnetclientclock.h: clock that synchronizes itself to a time provider over - * the network - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - - -#ifndef __GST_NET_CLIENT_CLOCK_H__ -#define __GST_NET_CLIENT_CLOCK_H__ - -#include <gst/gst.h> -#include <gst/gstsystemclock.h> -#include <gst/net/net-prelude.h> - -G_BEGIN_DECLS - -#define GST_TYPE_NET_CLIENT_CLOCK \ - (gst_net_client_clock_get_type()) -#define GST_NET_CLIENT_CLOCK(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_NET_CLIENT_CLOCK,GstNetClientClock)) -#define GST_NET_CLIENT_CLOCK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_NET_CLIENT_CLOCK,GstNetClientClockClass)) -#define GST_IS_NET_CLIENT_CLOCK(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_NET_CLIENT_CLOCK)) -#define GST_IS_NET_CLIENT_CLOCK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_NET_CLIENT_CLOCK)) - -typedef struct _GstNetClientClock GstNetClientClock; -typedef struct _GstNetClientClockClass GstNetClientClockClass; -typedef struct _GstNetClientClockPrivate GstNetClientClockPrivate; - -/** - * GstNetClientClock: - * - * Opaque #GstNetClientClock structure. - */ -struct _GstNetClientClock { - GstSystemClock clock; - - /*< private >*/ - GstNetClientClockPrivate *priv; - - gpointer _gst_reserved[GST_PADDING]; -}; - -struct _GstNetClientClockClass { - GstSystemClockClass parent_class; - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; -}; - -GST_NET_API -GType gst_net_client_clock_get_type (void); - -GST_NET_API -GstClock* gst_net_client_clock_new (const gchar *name, const gchar *remote_address, - gint remote_port, GstClockTime base_time); - -#define GST_TYPE_NTP_CLOCK \ - (gst_ntp_clock_get_type()) -#define GST_NTP_CLOCK(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_NTP_CLOCK,GstNtpClock)) -#define GST_NTP_CLOCK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_NTP_CLOCK,GstNtpClockClass)) -#define GST_IS_NTP_CLOCK(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_NTP_CLOCK)) -#define GST_IS_NTP_CLOCK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_NTP_CLOCK)) - -typedef struct _GstNetClientClock GstNtpClock; -typedef struct _GstNetClientClockClass GstNtpClockClass; - -GST_NET_API -GType gst_ntp_clock_get_type (void); - -GST_NET_API -GstClock* gst_ntp_clock_new (const gchar *name, const gchar *remote_address, - gint remote_port, GstClockTime base_time); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstNetClientClock, gst_object_unref) - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstNtpClock, gst_object_unref) - -G_END_DECLS - -#endif /* __GST_NET_CLIENT_CLOCK_H__ */ diff --git a/libs/gst/net/gstnetcontrolmessagemeta.c b/libs/gst/net/gstnetcontrolmessagemeta.c deleted file mode 100644 index f4d2a2912f..0000000000 --- a/libs/gst/net/gstnetcontrolmessagemeta.c +++ /dev/null @@ -1,132 +0,0 @@ -/* GStreamer - * Copyright (C) <2014> William Manley <will@williammanley.net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/** - * SECTION:gstnetcontrolmessagemeta - * @title: GstNetControlMessageMeta - * @short_description: Network Control Message Meta - * - * #GstNetControlMessageMeta can be used to store control messages (ancillary - * data) which was received with or is to be sent alongside the buffer data. - * When used with socket sinks and sources which understand this meta it allows - * sending and receiving ancillary data such as unix credentials (See - * #GUnixCredentialsMessage) and Unix file descriptions (See #GUnixFDMessage). - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <string.h> - -#include "gstnetcontrolmessagemeta.h" - -static gboolean -net_control_message_meta_init (GstMeta * meta, gpointer params, - GstBuffer * buffer) -{ - GstNetControlMessageMeta *nmeta = (GstNetControlMessageMeta *) meta; - - nmeta->message = NULL; - - return TRUE; -} - -static gboolean -net_control_message_meta_transform (GstBuffer * transbuf, GstMeta * meta, - GstBuffer * buffer, GQuark type, gpointer data) -{ - GstNetControlMessageMeta *smeta, *dmeta; - smeta = (GstNetControlMessageMeta *) meta; - - /* we always copy no matter what transform */ - dmeta = gst_buffer_add_net_control_message_meta (transbuf, smeta->message); - if (!dmeta) - return FALSE; - - return TRUE; -} - -static void -net_control_message_meta_free (GstMeta * meta, GstBuffer * buffer) -{ - GstNetControlMessageMeta *nmeta = (GstNetControlMessageMeta *) meta; - - if (nmeta->message) - g_object_unref (nmeta->message); - nmeta->message = NULL; -} - -GType -gst_net_control_message_meta_api_get_type (void) -{ - static GType type; - static const gchar *tags[] = { "origin", NULL }; - - if (g_once_init_enter (&type)) { - GType _type = - gst_meta_api_type_register ("GstNetControlMessageMetaAPI", tags); - g_once_init_leave (&type, _type); - } - return type; -} - -const GstMetaInfo * -gst_net_control_message_meta_get_info (void) -{ - static const GstMetaInfo *meta_info = NULL; - - if (g_once_init_enter ((GstMetaInfo **) & meta_info)) { - const GstMetaInfo *mi = - gst_meta_register (GST_NET_CONTROL_MESSAGE_META_API_TYPE, - "GstNetControlMessageMeta", - sizeof (GstNetControlMessageMeta), - net_control_message_meta_init, - net_control_message_meta_free, - net_control_message_meta_transform); - g_once_init_leave ((GstMetaInfo **) & meta_info, (GstMetaInfo *) mi); - } - return meta_info; -} - -/** - * gst_buffer_add_net_control_message_meta: - * @buffer: a #GstBuffer - * @message: a @GSocketControlMessage to attach to @buffer - * - * Attaches @message as metadata in a #GstNetControlMessageMeta to @buffer. - * - * Returns: (transfer none): a #GstNetControlMessageMeta connected to @buffer - */ -GstNetControlMessageMeta * -gst_buffer_add_net_control_message_meta (GstBuffer * buffer, - GSocketControlMessage * message) -{ - GstNetControlMessageMeta *meta; - - g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); - g_return_val_if_fail (G_IS_SOCKET_CONTROL_MESSAGE (message), NULL); - - meta = - (GstNetControlMessageMeta *) gst_buffer_add_meta (buffer, - GST_NET_CONTROL_MESSAGE_META_INFO, NULL); - - meta->message = g_object_ref (message); - - return meta; -} diff --git a/libs/gst/net/gstnetcontrolmessagemeta.h b/libs/gst/net/gstnetcontrolmessagemeta.h deleted file mode 100644 index b51b87683c..0000000000 --- a/libs/gst/net/gstnetcontrolmessagemeta.h +++ /dev/null @@ -1,69 +0,0 @@ -/* GStreamer - * Copyright (C) <2014> William Manley <will@williammanley.net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_NET_CONTROL_MESSAGE_META_H__ -#define __GST_NET_CONTROL_MESSAGE_META_H__ - -#include <gst/gst.h> -#include <gio/gio.h> -#include <gst/net/net-prelude.h> - -G_BEGIN_DECLS - -typedef struct _GstNetControlMessageMeta GstNetControlMessageMeta; - -/** - * GstNetControlMessageMeta: - * @meta: the parent type - * @message: a #GSocketControlMessage stored as metadata - * - * Buffer metadata for GSocket control messages, AKA ancillary data attached to - * data sent across a socket. - */ -struct _GstNetControlMessageMeta { - GstMeta meta; - - GSocketControlMessage *message; -}; - -GST_NET_API -GType gst_net_control_message_meta_api_get_type (void); - -#define GST_NET_CONTROL_MESSAGE_META_API_TYPE \ - (gst_net_control_message_meta_api_get_type()) - -#define gst_buffer_get_net_control_message_meta(b) ((GstNetControlMessageMeta*)\ - gst_buffer_get_meta((b),GST_NET_CONTROL_MESSAGE_META_API_TYPE)) - -/* implementation */ - -GST_NET_API -const GstMetaInfo *gst_net_control_message_meta_get_info (void); - -#define GST_NET_CONTROL_MESSAGE_META_INFO \ - (gst_net_control_message_meta_get_info()) - -GST_NET_API -GstNetControlMessageMeta * gst_buffer_add_net_control_message_meta (GstBuffer * buffer, - GSocketControlMessage * message); - -G_END_DECLS - -#endif /* __GST_NET_CONTROL_MESSAGE_META_H__ */ - diff --git a/libs/gst/net/gstnettimepacket.c b/libs/gst/net/gstnettimepacket.c deleted file mode 100644 index f914988d31..0000000000 --- a/libs/gst/net/gstnettimepacket.c +++ /dev/null @@ -1,247 +0,0 @@ -/* GStreamer - * Copyright (C) 2005 Andy Wingo <wingo@pobox.com> - * Copyright (C) 2010 Tim-Philipp Müller <tim centricular net> - * Copyright (C) 2012 Collabora Ltd. <tim.muller@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., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ -/** - * SECTION:gstnettimepacket - * @title: GstNetTimePacket - * @short_description: Helper structure to construct clock packets used - * by network clocks. - * @see_also: #GstClock, #GstNetClientClock, #GstNetTimeProvider - * - * Various functions for receiving, sending an serializing #GstNetTimePacket - * structures. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <glib.h> - -#ifdef __CYGWIN__ -# include <unistd.h> -# include <fcntl.h> -#endif - -#include "gstnettimepacket.h" - -G_DEFINE_BOXED_TYPE (GstNetTimePacket, gst_net_time_packet, - gst_net_time_packet_copy, gst_net_time_packet_free); - -/** - * gst_net_time_packet_new: - * @buffer: (array): a buffer from which to construct the packet, or NULL - * - * Creates a new #GstNetTimePacket from a buffer received over the network. The - * caller is responsible for ensuring that @buffer is at least - * #GST_NET_TIME_PACKET_SIZE bytes long. - * - * If @buffer is %NULL, the local and remote times will be set to - * #GST_CLOCK_TIME_NONE. - * - * MT safe. Caller owns return value (gst_net_time_packet_free to free). - * - * Returns: The new #GstNetTimePacket. - */ -GstNetTimePacket * -gst_net_time_packet_new (const guint8 * buffer) -{ - GstNetTimePacket *ret; - - g_assert (sizeof (GstClockTime) == 8); - - ret = g_new0 (GstNetTimePacket, 1); - - if (buffer) { - ret->local_time = GST_READ_UINT64_BE (buffer); - ret->remote_time = GST_READ_UINT64_BE (buffer + sizeof (GstClockTime)); - } else { - ret->local_time = GST_CLOCK_TIME_NONE; - ret->remote_time = GST_CLOCK_TIME_NONE; - } - - return ret; -} - -/** - * gst_net_time_packet_free: - * @packet: the #GstNetTimePacket - * - * Free @packet. - */ -void -gst_net_time_packet_free (GstNetTimePacket * packet) -{ - g_free (packet); -} - -/** - * gst_net_time_packet_copy: - * @packet: the #GstNetTimePacket - * - * Make a copy of @packet. - * - * Returns: a copy of @packet, free with gst_net_time_packet_free(). - */ -GstNetTimePacket * -gst_net_time_packet_copy (const GstNetTimePacket * packet) -{ - GstNetTimePacket *ret; - - ret = g_new0 (GstNetTimePacket, 1); - ret->local_time = packet->local_time; - ret->remote_time = packet->remote_time; - - return ret; -} - -/** - * gst_net_time_packet_serialize: - * @packet: the #GstNetTimePacket - * - * Serialized a #GstNetTimePacket into a newly-allocated sequence of - * #GST_NET_TIME_PACKET_SIZE bytes, in network byte order. The value returned is - * suitable for passing to write(2) or sendto(2) for communication over the - * network. - * - * MT safe. Caller owns return value (g_free to free). - * - * Returns: A newly allocated sequence of #GST_NET_TIME_PACKET_SIZE bytes. - */ -guint8 * -gst_net_time_packet_serialize (const GstNetTimePacket * packet) -{ - guint8 *ret; - - g_assert (sizeof (GstClockTime) == 8); - - ret = g_new0 (guint8, GST_NET_TIME_PACKET_SIZE); - - GST_WRITE_UINT64_BE (ret, packet->local_time); - GST_WRITE_UINT64_BE (ret + sizeof (GstClockTime), packet->remote_time); - - return ret; -} - -/** - * gst_net_time_packet_receive: - * @socket: socket to receive the time packet on - * @src_address: (out): address of variable to return sender address - * @error: return address for a #GError, or NULL - * - * Receives a #GstNetTimePacket over a socket. Handles interrupted system - * calls, but otherwise returns NULL on error. - * - * Returns: (transfer full): a new #GstNetTimePacket, or NULL on error. Free - * with gst_net_time_packet_free() when done. - */ -GstNetTimePacket * -gst_net_time_packet_receive (GSocket * socket, - GSocketAddress ** src_address, GError ** error) -{ - gchar buffer[GST_NET_TIME_PACKET_SIZE]; - GError *err = NULL; - gssize ret; - - g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - while (TRUE) { - ret = g_socket_receive_from (socket, src_address, buffer, - GST_NET_TIME_PACKET_SIZE, NULL, &err); - - if (ret < 0) { - if (err->code == G_IO_ERROR_WOULD_BLOCK) { - g_error_free (err); - err = NULL; - continue; - } else { - goto receive_error; - } - } else if (ret < GST_NET_TIME_PACKET_SIZE) { - goto short_packet; - } else { - return gst_net_time_packet_new ((const guint8 *) buffer); - } - } - -receive_error: - { - GST_DEBUG ("receive error: %s", err->message); - g_propagate_error (error, err); - return NULL; - } -short_packet: - { - GST_DEBUG ("someone sent us a short packet (%" G_GSSIZE_FORMAT " < %d)", - ret, GST_NET_TIME_PACKET_SIZE); - g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, - "short time packet (%d < %d)", (int) ret, GST_NET_TIME_PACKET_SIZE); - return NULL; - } -} - -/** - * gst_net_time_packet_send: - * @packet: the #GstNetTimePacket to send - * @socket: socket to send the time packet on - * @dest_address: address to send the time packet to - * @error: return address for a #GError, or NULL - * - * Sends a #GstNetTimePacket over a socket. - * - * MT safe. - * - * Returns: TRUE if successful, FALSE in case an error occurred. - */ -gboolean -gst_net_time_packet_send (const GstNetTimePacket * packet, - GSocket * socket, GSocketAddress * dest_address, GError ** error) -{ - gboolean was_blocking; - guint8 *buffer; - gssize res; - - g_return_val_if_fail (packet != NULL, FALSE); - g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); - g_return_val_if_fail (G_IS_SOCKET_ADDRESS (dest_address), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - was_blocking = g_socket_get_blocking (socket); - - if (was_blocking) - g_socket_set_blocking (socket, FALSE); - - /* FIXME: avoid pointless alloc/free, serialise into stack-allocated buffer */ - buffer = gst_net_time_packet_serialize (packet); - - res = g_socket_send_to (socket, dest_address, (const gchar *) buffer, - GST_NET_TIME_PACKET_SIZE, NULL, error); - - /* datagram packets should be sent as a whole or not at all */ - g_assert (res < 0 || res == GST_NET_TIME_PACKET_SIZE); - - g_free (buffer); - - if (was_blocking) - g_socket_set_blocking (socket, TRUE); - - return (res == GST_NET_TIME_PACKET_SIZE); -} diff --git a/libs/gst/net/gstnettimepacket.h b/libs/gst/net/gstnettimepacket.h deleted file mode 100644 index a8f2f661bd..0000000000 --- a/libs/gst/net/gstnettimepacket.h +++ /dev/null @@ -1,81 +0,0 @@ -/* GStreamer - * Copyright (C) 2005 Andy Wingo <wingo@pobox.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - - -#ifndef __GST_NET_TIME_PACKET_H__ -#define __GST_NET_TIME_PACKET_H__ - -#include <gst/gst.h> -#include <gio/gio.h> -#include <gst/net/net-prelude.h> - -G_BEGIN_DECLS - -/** - * GST_NET_TIME_PACKET_SIZE: - * - * The size of the packets sent between network clocks. - */ -#define GST_NET_TIME_PACKET_SIZE 16 - -typedef struct _GstNetTimePacket GstNetTimePacket; - -/** - * GstNetTimePacket: - * @local_time: the local time when this packet was sent - * @remote_time: the remote time observation - * - * Content of a #GstNetTimePacket. - */ -struct _GstNetTimePacket { - GstClockTime local_time; - GstClockTime remote_time; -}; - -GST_NET_API -GType gst_net_time_packet_get_type (void); - -GST_NET_API -GstNetTimePacket* gst_net_time_packet_new (const guint8 *buffer); - -GST_NET_API -GstNetTimePacket* gst_net_time_packet_copy (const GstNetTimePacket *packet); - -GST_NET_API -void gst_net_time_packet_free (GstNetTimePacket *packet); - -GST_NET_API -guint8* gst_net_time_packet_serialize (const GstNetTimePacket *packet); - -GST_NET_API -GstNetTimePacket* gst_net_time_packet_receive (GSocket * socket, - GSocketAddress ** src_address, - GError ** error); -GST_NET_API -gboolean gst_net_time_packet_send (const GstNetTimePacket * packet, - GSocket * socket, - GSocketAddress * dest_address, - GError ** error); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstNetTimePacket, gst_net_time_packet_free) - -G_END_DECLS - - -#endif /* __GST_NET_TIME_PACKET_H__ */ diff --git a/libs/gst/net/gstnettimeprovider.c b/libs/gst/net/gstnettimeprovider.c deleted file mode 100644 index 1df6959f02..0000000000 --- a/libs/gst/net/gstnettimeprovider.c +++ /dev/null @@ -1,477 +0,0 @@ -/* GStreamer - * Copyright (C) 2005 Andy Wingo <wingo@pobox.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ -/** - * SECTION:gstnettimeprovider - * @title: GstNetTimeProvider - * @short_description: Special object that exposed the time of a clock - * on the network. - * @see_also: #GstClock, #GstNetClientClock, #GstPipeline - * - * This object exposes the time of a #GstClock on the network. - * - * A #GstNetTimeProvider is created with gst_net_time_provider_new() which - * takes a #GstClock, an address and a port number as arguments. - * - * After creating the object, a client clock such as #GstNetClientClock can - * query the exposed clock over the network for its values. - * - * The #GstNetTimeProvider typically wraps the clock used by a #GstPipeline. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstnettimeprovider.h" -#include "gstnettimepacket.h" -#include "gstnetutils.h" - -GST_DEBUG_CATEGORY_STATIC (ntp_debug); -#define GST_CAT_DEFAULT (ntp_debug) - -#define DEFAULT_ADDRESS "0.0.0.0" -#define DEFAULT_PORT 5637 -#define DEFAULT_QOS_DSCP -1 - -#define IS_ACTIVE(self) (g_atomic_int_get (&((self)->priv->active))) - -enum -{ - PROP_0, - PROP_PORT, - PROP_ADDRESS, - PROP_CLOCK, - PROP_ACTIVE, - PROP_QOS_DSCP -}; - -struct _GstNetTimeProviderPrivate -{ - gchar *address; - int port; - gint qos_dscp; /* ATOMIC */ - - GThread *thread; - - GstClock *clock; - - gboolean active; /* ATOMIC */ - - GSocket *socket; - GCancellable *cancel; - gboolean made_cancel_fd; -}; - -static void gst_net_time_provider_initable_iface_init (gpointer g_iface); - -static gboolean gst_net_time_provider_start (GstNetTimeProvider * bself, - GError ** error); -static void gst_net_time_provider_stop (GstNetTimeProvider * bself); - -static gpointer gst_net_time_provider_thread (gpointer data); - -static void gst_net_time_provider_finalize (GObject * object); -static void gst_net_time_provider_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_net_time_provider_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -#define _do_init \ - GST_DEBUG_CATEGORY_INIT (ntp_debug, "nettime", 0, "Network time provider"); \ - G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gst_net_time_provider_initable_iface_init) - -#define gst_net_time_provider_parent_class parent_class -G_DEFINE_TYPE_WITH_CODE (GstNetTimeProvider, gst_net_time_provider, - GST_TYPE_OBJECT, G_ADD_PRIVATE (GstNetTimeProvider) _do_init); - -static void -gst_net_time_provider_class_init (GstNetTimeProviderClass * klass) -{ - GObjectClass *gobject_class; - - gobject_class = G_OBJECT_CLASS (klass); - - g_assert (sizeof (GstClockTime) == 8); - - gobject_class->finalize = gst_net_time_provider_finalize; - gobject_class->set_property = gst_net_time_provider_set_property; - gobject_class->get_property = gst_net_time_provider_get_property; - - g_object_class_install_property (gobject_class, PROP_PORT, - g_param_spec_int ("port", "port", - "The port to receive the packets from, 0=allocate", 0, G_MAXUINT16, - DEFAULT_PORT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_ADDRESS, - g_param_spec_string ("address", "address", - "The address to bind on, as a dotted quad (x.x.x.x)", DEFAULT_ADDRESS, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_CLOCK, - g_param_spec_object ("clock", "Clock", - "The clock to export over the network", GST_TYPE_CLOCK, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_ACTIVE, - g_param_spec_boolean ("active", "Active", - "TRUE if the clock will respond to queries over the network", TRUE, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_QOS_DSCP, - g_param_spec_int ("qos-dscp", "QoS diff srv code point", - "Quality of Service, differentiated services code point (-1 default)", - -1, 63, DEFAULT_QOS_DSCP, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -} - -static void -gst_net_time_provider_init (GstNetTimeProvider * self) -{ - self->priv = gst_net_time_provider_get_instance_private (self); - - self->priv->port = DEFAULT_PORT; - self->priv->address = g_strdup (DEFAULT_ADDRESS); - self->priv->qos_dscp = DEFAULT_QOS_DSCP; - self->priv->thread = NULL; - self->priv->active = TRUE; -} - -static void -gst_net_time_provider_finalize (GObject * object) -{ - GstNetTimeProvider *self = GST_NET_TIME_PROVIDER (object); - - if (self->priv->thread) { - gst_net_time_provider_stop (self); - g_assert (self->priv->thread == NULL); - } - - g_free (self->priv->address); - self->priv->address = NULL; - - if (self->priv->clock) - gst_object_unref (self->priv->clock); - self->priv->clock = NULL; - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static gpointer -gst_net_time_provider_thread (gpointer data) -{ - GstNetTimeProvider *self = data; - GCancellable *cancel = self->priv->cancel; - GSocket *socket = self->priv->socket; - GstNetTimePacket *packet; - GError *err = NULL; - gint cur_qos_dscp = DEFAULT_QOS_DSCP; - gint new_qos_dscp; - - GST_INFO_OBJECT (self, "time provider thread is running"); - - while (TRUE) { - GSocketAddress *sender_addr = NULL; - - - GST_LOG_OBJECT (self, "waiting on socket"); - if (!g_socket_condition_wait (socket, G_IO_IN, cancel, &err)) { - GST_INFO_OBJECT (self, "socket error: %s", err->message); - - if (err->code == G_IO_ERROR_CANCELLED) - break; - - /* try again */ - g_usleep (G_USEC_PER_SEC / 10); - g_error_free (err); - err = NULL; - continue; - } - - /* got data in */ - packet = gst_net_time_packet_receive (socket, &sender_addr, &err); - - if (err != NULL) { - GST_DEBUG_OBJECT (self, "receive error: %s", err->message); - g_usleep (G_USEC_PER_SEC / 10); - g_error_free (err); - err = NULL; - continue; - } - - /* before next sending check if need to change QoS */ - new_qos_dscp = self->priv->qos_dscp; - if (cur_qos_dscp != new_qos_dscp && - gst_net_utils_set_socket_tos (socket, new_qos_dscp)) { - GST_DEBUG_OBJECT (self, "changed QoS DSCP to: %d", new_qos_dscp); - cur_qos_dscp = new_qos_dscp; - } - - if (IS_ACTIVE (self)) { - /* do what we were asked to and send the packet back */ - packet->remote_time = gst_clock_get_time (self->priv->clock); - - /* ignore errors */ - gst_net_time_packet_send (packet, socket, sender_addr, NULL); - g_object_unref (sender_addr); - g_free (packet); - } - } - - g_error_free (err); - - GST_INFO_OBJECT (self, "time provider thread is stopping"); - return NULL; -} - -static void -gst_net_time_provider_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstNetTimeProvider *self = GST_NET_TIME_PROVIDER (object); - GstClock **clock_p = &self->priv->clock; - - switch (prop_id) { - case PROP_PORT: - self->priv->port = g_value_get_int (value); - break; - case PROP_ADDRESS: - g_free (self->priv->address); - if (g_value_get_string (value) == NULL) - self->priv->address = g_strdup (DEFAULT_ADDRESS); - else - self->priv->address = g_value_dup_string (value); - break; - case PROP_CLOCK: - gst_object_replace ((GstObject **) clock_p, - (GstObject *) g_value_get_object (value)); - break; - case PROP_ACTIVE: - g_atomic_int_set (&self->priv->active, g_value_get_boolean (value)); - break; - case PROP_QOS_DSCP: - g_atomic_int_set (&self->priv->qos_dscp, g_value_get_int (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_net_time_provider_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstNetTimeProvider *self = GST_NET_TIME_PROVIDER (object); - - switch (prop_id) { - case PROP_PORT: - g_value_set_int (value, self->priv->port); - break; - case PROP_ADDRESS: - g_value_set_string (value, self->priv->address); - break; - case PROP_CLOCK: - g_value_set_object (value, self->priv->clock); - break; - case PROP_ACTIVE: - g_value_set_boolean (value, IS_ACTIVE (self)); - break; - case PROP_QOS_DSCP: - g_value_set_int (value, self->priv->qos_dscp); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static gboolean -gst_net_time_provider_start (GstNetTimeProvider * self, GError ** error) -{ - GSocketAddress *socket_addr, *bound_addr; - GInetAddress *inet_addr; - GPollFD dummy_pollfd; - GSocket *socket; - int port; - gchar *address; - GError *err = NULL; - - if (self->priv->address) { - inet_addr = g_inet_address_new_from_string (self->priv->address); - if (inet_addr == NULL) { - err = - g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, - "Failed to parse address '%s'", self->priv->address); - goto invalid_address; - } - } else { - inet_addr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4); - } - - GST_LOG_OBJECT (self, "creating socket"); - socket = g_socket_new (g_inet_address_get_family (inet_addr), - G_SOCKET_TYPE_DATAGRAM, G_SOCKET_PROTOCOL_UDP, &err); - - if (!socket) - goto no_socket; - - GST_DEBUG_OBJECT (self, "binding on port %d", self->priv->port); - socket_addr = g_inet_socket_address_new (inet_addr, self->priv->port); - if (!g_socket_bind (socket, socket_addr, TRUE, &err)) { - g_object_unref (socket_addr); - g_object_unref (inet_addr); - goto bind_error; - } - g_object_unref (socket_addr); - g_object_unref (inet_addr); - - bound_addr = g_socket_get_local_address (socket, NULL); - port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (bound_addr)); - inet_addr = - g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (bound_addr)); - address = g_inet_address_to_string (inet_addr); - - if (g_strcmp0 (address, self->priv->address)) { - g_free (self->priv->address); - self->priv->address = address; - GST_DEBUG_OBJECT (self, "notifying address %s", address); - g_object_notify (G_OBJECT (self), "address"); - } else { - g_free (address); - } - if (port != self->priv->port) { - self->priv->port = port; - GST_DEBUG_OBJECT (self, "notifying port %d", port); - g_object_notify (G_OBJECT (self), "port"); - } - GST_DEBUG_OBJECT (self, "bound on UDP address %s, port %d", - self->priv->address, port); - g_object_unref (bound_addr); - - self->priv->socket = socket; - self->priv->cancel = g_cancellable_new (); - self->priv->made_cancel_fd = - g_cancellable_make_pollfd (self->priv->cancel, &dummy_pollfd); - - self->priv->thread = g_thread_try_new ("GstNetTimeProvider", - gst_net_time_provider_thread, self, &err); - - if (!self->priv->thread) - goto no_thread; - - return TRUE; - - /* ERRORS */ -invalid_address: - { - GST_ERROR_OBJECT (self, "invalid address: %s", self->priv->address); - g_propagate_error (error, err); - return FALSE; - } -no_socket: - { - GST_ERROR_OBJECT (self, "could not create socket: %s", err->message); - g_propagate_error (error, err); - g_object_unref (inet_addr); - return FALSE; - } -bind_error: - { - GST_ERROR_OBJECT (self, "bind failed: %s", err->message); - g_propagate_error (error, err); - g_object_unref (socket); - return FALSE; - } -no_thread: - { - GST_ERROR_OBJECT (self, "could not create thread: %s", err->message); - g_propagate_error (error, err); - g_object_unref (self->priv->socket); - self->priv->socket = NULL; - g_object_unref (self->priv->cancel); - self->priv->cancel = NULL; - return FALSE; - } -} - -static void -gst_net_time_provider_stop (GstNetTimeProvider * self) -{ - g_return_if_fail (self->priv->thread != NULL); - - GST_INFO_OBJECT (self, "stopping.."); - g_cancellable_cancel (self->priv->cancel); - - g_thread_join (self->priv->thread); - self->priv->thread = NULL; - - if (self->priv->made_cancel_fd) - g_cancellable_release_fd (self->priv->cancel); - - g_object_unref (self->priv->cancel); - self->priv->cancel = NULL; - - g_object_unref (self->priv->socket); - self->priv->socket = NULL; - - GST_INFO_OBJECT (self, "stopped"); -} - -static gboolean -gst_net_time_provider_initable_init (GInitable * initable, - GCancellable * cancellable, GError ** error) -{ - GstNetTimeProvider *self = GST_NET_TIME_PROVIDER (initable); - - return gst_net_time_provider_start (self, error); -} - -static void -gst_net_time_provider_initable_iface_init (gpointer g_iface) -{ - GInitableIface *iface = g_iface; - - iface->init = gst_net_time_provider_initable_init; -} - -/** - * gst_net_time_provider_new: - * @clock: a #GstClock to export over the network - * @address: (allow-none): an address to bind on as a dotted quad - * (xxx.xxx.xxx.xxx), IPv6 address, or NULL to bind to all addresses - * @port: a port to bind on, or 0 to let the kernel choose - * - * Allows network clients to get the current time of @clock. - * - * Returns: (transfer full): the new #GstNetTimeProvider, or NULL on error - */ -GstNetTimeProvider * -gst_net_time_provider_new (GstClock * clock, const gchar * address, gint port) -{ - GstNetTimeProvider *ret; - - g_return_val_if_fail (clock && GST_IS_CLOCK (clock), NULL); - g_return_val_if_fail (port >= 0 && port <= G_MAXUINT16, NULL); - - ret = - g_initable_new (GST_TYPE_NET_TIME_PROVIDER, NULL, NULL, "clock", clock, - "address", address, "port", port, NULL); - - /* Clear floating flag */ - g_object_ref_sink (ret); - - return ret; -} diff --git a/libs/gst/net/gstnettimeprovider.h b/libs/gst/net/gstnettimeprovider.h deleted file mode 100644 index 40eeef318d..0000000000 --- a/libs/gst/net/gstnettimeprovider.h +++ /dev/null @@ -1,79 +0,0 @@ -/* GStreamer - * Copyright (C) 2005 Andy Wingo <wingo@pobox.com> - * 2006 Joni Valtanen <joni.valtanen@movial.fi> - * Copyright (C) 2012 Collabora Ltd. <tim.muller@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., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - - -#ifndef __GST_NET_TIME_PROVIDER_H__ -#define __GST_NET_TIME_PROVIDER_H__ - -#include <gst/gst.h> -#include <gst/net/net-prelude.h> - -G_BEGIN_DECLS - -#define GST_TYPE_NET_TIME_PROVIDER \ - (gst_net_time_provider_get_type()) -#define GST_NET_TIME_PROVIDER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_NET_TIME_PROVIDER,GstNetTimeProvider)) -#define GST_NET_TIME_PROVIDER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_NET_TIME_PROVIDER,GstNetTimeProviderClass)) -#define GST_IS_NET_TIME_PROVIDER(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_NET_TIME_PROVIDER)) -#define GST_IS_NET_TIME_PROVIDER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_NET_TIME_PROVIDER)) - -typedef struct _GstNetTimeProvider GstNetTimeProvider; -typedef struct _GstNetTimeProviderClass GstNetTimeProviderClass; -typedef struct _GstNetTimeProviderPrivate GstNetTimeProviderPrivate; - -/** - * GstNetTimeProvider: - * - * Opaque #GstNetTimeProvider structure. - */ -struct _GstNetTimeProvider { - GstObject parent; - - /*< private >*/ - GstNetTimeProviderPrivate *priv; - - gpointer _gst_reserved[GST_PADDING]; -}; - -struct _GstNetTimeProviderClass { - GstObjectClass parent_class; - - gpointer _gst_reserved[GST_PADDING]; -}; - -GST_NET_API -GType gst_net_time_provider_get_type (void); - -GST_NET_API -GstNetTimeProvider* gst_net_time_provider_new (GstClock *clock, - const gchar *address, - gint port); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstNetTimeProvider, gst_object_unref) - -G_END_DECLS - - -#endif /* __GST_NET_TIME_PROVIDER_H__ */ diff --git a/libs/gst/net/gstnetutils.c b/libs/gst/net/gstnetutils.c deleted file mode 100644 index 3390e7d65d..0000000000 --- a/libs/gst/net/gstnetutils.c +++ /dev/null @@ -1,88 +0,0 @@ -/* GStreamer - * Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com> - * Copyright (C) 2017 Robert Rosengren <robertr@axis.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ -/** - * SECTION:gstnetutils - * @title: GstNetUtils - * @short_description: Network utility functions. - * - * GstNetUtils gathers network utility functions, enabling use for all - * gstreamer plugins. - * - * Since: 1.18 - * - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstnetutils.h" -#include <gst/gstinfo.h> -#include <errno.h> - -#ifdef HAVE_SYS_SOCKET_H -#include <sys/socket.h> -#endif - -#ifndef G_OS_WIN32 -#include <netinet/in.h> -#endif - -/** - * gst_net_utils_set_socket_tos: - * @socket: Socket to configure - * @qos_dscp: QoS DSCP value - * - * Configures IP_TOS value of socket, i.e. sets QoS DSCP. - * - * Returns: TRUE if successful, FALSE in case an error occurred. - * - * Since: 1.18 - */ -gboolean -gst_net_utils_set_socket_tos (GSocket * socket, gint qos_dscp) -{ - gboolean ret = FALSE; - -#ifdef IP_TOS - gint tos, fd; - fd = g_socket_get_fd (socket); - - /* Extract and shift 6 bits of DSFIELD */ - tos = (qos_dscp & 0x3f) << 2; - - if (setsockopt (fd, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)) < 0) { - GST_ERROR ("could not set TOS: %s", g_strerror (errno)); - } else { - ret = TRUE; - } -#ifdef IPV6_TCLASS - if (g_socket_get_family (socket) == G_SOCKET_FAMILY_IPV6) { - if (setsockopt (fd, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof (tos)) < 0) { - GST_ERROR ("could not set TCLASS: %s", g_strerror (errno)); - } else { - ret = TRUE; - } - } -#endif -#endif - - return ret; -} diff --git a/libs/gst/net/gstnetutils.h b/libs/gst/net/gstnetutils.h deleted file mode 100644 index d2b4043be8..0000000000 --- a/libs/gst/net/gstnetutils.h +++ /dev/null @@ -1,37 +0,0 @@ -/* GStreamer - * Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com> - * Copyright (C) 2017 Robert Rosengren <robertr@axis.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - - -#ifndef __GST_NET_UTILS_H__ -#define __GST_NET_UTILS_H__ - -#include <glib.h> -#include <gio/gio.h> -#include <gst/net/net-prelude.h> - -G_BEGIN_DECLS - -GST_NET_API -gboolean gst_net_utils_set_socket_tos (GSocket * socket, - gint qos_dscp); - -G_END_DECLS - -#endif /* __GST_NET_UTILS_H__ */ diff --git a/libs/gst/net/gstntppacket.c b/libs/gst/net/gstntppacket.c deleted file mode 100644 index 8d88d75c12..0000000000 --- a/libs/gst/net/gstntppacket.c +++ /dev/null @@ -1,376 +0,0 @@ -/* GStreamer - * Copyright (C) 2005 Andy Wingo <wingo@pobox.com> - * Copyright (C) 2010 Tim-Philipp Müller <tim centricular net> - * Copyright (C) 2012 Collabora Ltd. <tim.muller@collabora.co.uk> - * Copyright (C) 2015 Sebastian Dröge <sebastian@centricular.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ -/* THIS IS A PRIVATE API - * SECTION:gstntppacket - * @short_description: Helper structure to construct clock packets used - * by network clocks for NTPv4. - * @see_also: #GstClock, #GstNetClientClock, #GstNtpClock - * - * Various functions for receiving, sending an serializing #GstNtpPacket - * structures. - */ - -/* FIXME 2.0: Merge this with GstNetTimePacket! */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <glib.h> - -#ifdef __CYGWIN__ -# include <unistd.h> -# include <fcntl.h> -#endif - -#include <gst/gst.h> -#include <string.h> - -#include "gstntppacket.h" - -G_DEFINE_BOXED_TYPE (GstNtpPacket, gst_ntp_packet, - gst_ntp_packet_copy, gst_ntp_packet_free); - - -static inline GstClockTime -ntp_timestamp_to_gst_clock_time (guint32 seconds, guint32 fraction) -{ - return gst_util_uint64_scale (seconds, GST_SECOND, 1) + - gst_util_uint64_scale (fraction, GST_SECOND, - G_GUINT64_CONSTANT (1) << 32); -} - -static inline guint32 -gst_clock_time_to_ntp_timestamp_seconds (GstClockTime gst) -{ - GstClockTime seconds = gst_util_uint64_scale (gst, 1, GST_SECOND); - - return seconds; -} - -static inline guint32 -gst_clock_time_to_ntp_timestamp_fraction (GstClockTime gst) -{ - GstClockTime seconds = gst_util_uint64_scale (gst, 1, GST_SECOND); - - return gst_util_uint64_scale (gst - seconds, G_GUINT64_CONSTANT (1) << 32, - GST_SECOND); -} - -/** - * gst_ntp_packet_new: - * @buffer: (array): a buffer from which to construct the packet, or NULL - * @error: a #GError - * - * Creates a new #GstNtpPacket from a buffer received over the network. The - * caller is responsible for ensuring that @buffer is at least - * #GST_NTP_PACKET_SIZE bytes long. - * - * If @buffer is %NULL, the local and remote times will be set to - * #GST_CLOCK_TIME_NONE. - * - * MT safe. Caller owns return value (gst_ntp_packet_free to free). - * - * Returns: The new #GstNtpPacket. - */ -GstNtpPacket * -gst_ntp_packet_new (const guint8 * buffer, GError ** error) -{ - GstNtpPacket *ret; - - g_assert (sizeof (GstClockTime) == 8); - - if (buffer) { - guint8 version = (buffer[0] >> 3) & 0x7; - guint8 stratum = buffer[1]; - gint8 poll_interval = buffer[2]; - - if (version != 4) { - g_set_error (error, GST_NTP_ERROR, GST_NTP_ERROR_WRONG_VERSION, - "Invalid NTP version %d", version); - return NULL; - } - - /* Kiss-o'-Death packet! */ - if (stratum == 0) { - gchar code[5] = { buffer[3 * 4 + 0], buffer[3 * 4 + 1], buffer[3 * 4 + 2], - buffer[3 * 4 + 3], 0 - }; - - /* AUTH, AUTO, CRYP, DENY, RSTR, NKEY => DENY */ - if (strcmp (code, "AUTH") == 0 || - strcmp (code, "AUTO") == 0 || - strcmp (code, "CRYP") == 0 || - strcmp (code, "DENY") == 0 || - strcmp (code, "RSTR") == 0 || strcmp (code, "NKEY") == 0) { - g_set_error (error, GST_NTP_ERROR, GST_NTP_ERROR_KOD_DENY, - "Kiss-o'-Death denied '%s'", code); - } else if (strcmp (code, "RATE") == 0) { - g_set_error (error, GST_NTP_ERROR, GST_NTP_ERROR_KOD_RATE, - "Kiss-o'-Death '%s'", code); - } else { - g_set_error (error, GST_NTP_ERROR, GST_NTP_ERROR_KOD_UNKNOWN, - "Kiss-o'-Death unknown '%s'", code); - } - - return NULL; - } - - ret = g_new0 (GstNtpPacket, 1); - ret->origin_time = - ntp_timestamp_to_gst_clock_time (GST_READ_UINT32_BE (buffer + 6 * 4), - GST_READ_UINT32_BE (buffer + 7 * 4)); - ret->receive_time = - ntp_timestamp_to_gst_clock_time (GST_READ_UINT32_BE (buffer + 8 * 4), - GST_READ_UINT32_BE (buffer + 9 * 4)); - ret->transmit_time = - ntp_timestamp_to_gst_clock_time (GST_READ_UINT32_BE (buffer + 10 * 4), - GST_READ_UINT32_BE (buffer + 11 * 4)); - - /* Wireshark considers everything >= 3 as invalid */ - if (poll_interval >= 3) - ret->poll_interval = GST_CLOCK_TIME_NONE; - else if (poll_interval >= 0) - ret->poll_interval = GST_SECOND << poll_interval; - else - ret->poll_interval = GST_SECOND >> (-poll_interval); - } else { - ret = g_new0 (GstNtpPacket, 1); - ret->origin_time = 0; - ret->receive_time = 0; - ret->transmit_time = 0; - ret->poll_interval = 0; - } - - return ret; -} - -/** - * gst_ntp_packet_free: - * @packet: the #GstNtpPacket - * - * Free @packet. - */ -void -gst_ntp_packet_free (GstNtpPacket * packet) -{ - g_free (packet); -} - -/** - * gst_ntp_packet_copy: - * @packet: the #GstNtpPacket - * - * Make a copy of @packet. - * - * Returns: a copy of @packet, free with gst_ntp_packet_free(). - */ -GstNtpPacket * -gst_ntp_packet_copy (const GstNtpPacket * packet) -{ - GstNtpPacket *ret; - - ret = g_new0 (GstNtpPacket, 1); - ret->origin_time = packet->origin_time; - ret->receive_time = packet->receive_time; - ret->transmit_time = packet->transmit_time; - - return ret; -} - -/** - * gst_ntp_packet_serialize: - * @packet: the #GstNtpPacket - * - * Serialized a #GstNtpPacket into a newly-allocated sequence of - * #GST_NTP_PACKET_SIZE bytes, in network byte order. The value returned is - * suitable for passing to write(2) or sendto(2) for communication over the - * network. - * - * MT safe. Caller owns return value (g_free to free). - * - * Returns: A newly allocated sequence of #GST_NTP_PACKET_SIZE bytes. - */ -guint8 * -gst_ntp_packet_serialize (const GstNtpPacket * packet) -{ - guint8 *ret; - - g_assert (sizeof (GstClockTime) == 8); - - ret = g_new0 (guint8, GST_NTP_PACKET_SIZE); - /* Leap Indicator: unknown - * Version: 4 - * Mode: Client - */ - ret[0] = (3 << 6) | (4 << 3) | (3 << 0); - /* Stratum: unsynchronized */ - ret[1] = 16; - /* Polling interval: invalid */ - ret[2] = 3; - /* Precision: 0 */ - ret[3] = 0; - /* Root delay: 0 */ - GST_WRITE_UINT32_BE (ret + 4, 0); - /* Root disperson: 0 */ - GST_WRITE_UINT32_BE (ret + 2 * 4, 0); - /* Reference ID: \0 */ - GST_WRITE_UINT32_BE (ret + 3 * 4, 0); - /* Reference Timestamp: 0 */ - GST_WRITE_UINT32_BE (ret + 4 * 4, 0); - GST_WRITE_UINT32_BE (ret + 5 * 4, 0); - /* Origin timestamp (local time) */ - GST_WRITE_UINT32_BE (ret + 6 * 4, - gst_clock_time_to_ntp_timestamp_seconds (packet->origin_time)); - GST_WRITE_UINT32_BE (ret + 7 * 4, - gst_clock_time_to_ntp_timestamp_fraction (packet->origin_time)); - /* Receive timestamp (remote time) */ - GST_WRITE_UINT32_BE (ret + 8 * 4, - gst_clock_time_to_ntp_timestamp_seconds (packet->receive_time)); - GST_WRITE_UINT32_BE (ret + 9 * 4, - gst_clock_time_to_ntp_timestamp_fraction (packet->receive_time)); - /* Transmit timestamp (remote time) */ - GST_WRITE_UINT32_BE (ret + 10 * 4, - gst_clock_time_to_ntp_timestamp_seconds (packet->transmit_time)); - GST_WRITE_UINT32_BE (ret + 11 * 4, - gst_clock_time_to_ntp_timestamp_fraction (packet->transmit_time)); - - return ret; -} - -/** - * gst_ntp_packet_receive: - * @socket: socket to receive the time packet on - * @src_address: (out): address of variable to return sender address - * @error: return address for a #GError, or NULL - * - * Receives a #GstNtpPacket over a socket. Handles interrupted system - * calls, but otherwise returns NULL on error. - * - * Returns: (transfer full): a new #GstNtpPacket, or NULL on error. Free - * with gst_ntp_packet_free() when done. - */ -GstNtpPacket * -gst_ntp_packet_receive (GSocket * socket, - GSocketAddress ** src_address, GError ** error) -{ - gchar buffer[GST_NTP_PACKET_SIZE]; - GError *err = NULL; - gssize ret; - - g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - while (TRUE) { - ret = g_socket_receive_from (socket, src_address, buffer, - GST_NTP_PACKET_SIZE, NULL, &err); - - if (ret < 0) { - if (err->code == G_IO_ERROR_WOULD_BLOCK) { - g_error_free (err); - err = NULL; - continue; - } else { - goto receive_error; - } - } else if (ret < GST_NTP_PACKET_SIZE) { - goto short_packet; - } else { - return gst_ntp_packet_new ((const guint8 *) buffer, error); - } - } - -receive_error: - { - GST_DEBUG ("receive error: %s", err->message); - g_propagate_error (error, err); - return NULL; - } -short_packet: - { - GST_DEBUG ("someone sent us a short packet (%" G_GSSIZE_FORMAT " < %d)", - ret, GST_NTP_PACKET_SIZE); - g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, - "short time packet (%d < %d)", (int) ret, GST_NTP_PACKET_SIZE); - return NULL; - } -} - -/** - * gst_ntp_packet_send: - * @packet: the #GstNtpPacket to send - * @socket: socket to send the time packet on - * @dest_address: address to send the time packet to - * @error: return address for a #GError, or NULL - * - * Sends a #GstNtpPacket over a socket. - * - * MT safe. - * - * Returns: TRUE if successful, FALSE in case an error occurred. - */ -gboolean -gst_ntp_packet_send (const GstNtpPacket * packet, - GSocket * socket, GSocketAddress * dest_address, GError ** error) -{ - gboolean was_blocking; - guint8 *buffer; - gssize res; - - g_return_val_if_fail (packet != NULL, FALSE); - g_return_val_if_fail (G_IS_SOCKET (socket), FALSE); - g_return_val_if_fail (G_IS_SOCKET_ADDRESS (dest_address), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - was_blocking = g_socket_get_blocking (socket); - - if (was_blocking) - g_socket_set_blocking (socket, FALSE); - - /* FIXME: avoid pointless alloc/free, serialise into stack-allocated buffer */ - buffer = gst_ntp_packet_serialize (packet); - - res = g_socket_send_to (socket, dest_address, (const gchar *) buffer, - GST_NTP_PACKET_SIZE, NULL, error); - - /* datagram packets should be sent as a whole or not at all */ - g_assert (res < 0 || res == GST_NTP_PACKET_SIZE); - - g_free (buffer); - - if (was_blocking) - g_socket_set_blocking (socket, TRUE); - - return (res == GST_NTP_PACKET_SIZE); -} - -GQuark -gst_ntp_error_quark (void) -{ - static GQuark quark; - - /* Thread-safe because GQuark is */ - if (!quark) - quark = g_quark_from_static_string ("gst-ntp-error-quark"); - - return quark; -} diff --git a/libs/gst/net/gstntppacket.h b/libs/gst/net/gstntppacket.h deleted file mode 100644 index 0ecd5e881c..0000000000 --- a/libs/gst/net/gstntppacket.h +++ /dev/null @@ -1,86 +0,0 @@ -/* GStreamer - * Copyright (C) 2005 Andy Wingo <wingo@pobox.com> - * Copyright (C) 2015 Sebastian Dröge <sebastian@centricular.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - - -#ifndef __GST_NTP_PACKET_H__ -#define __GST_NTP_PACKET_H__ - -#include <gst/gst.h> -#include <gio/gio.h> - -G_BEGIN_DECLS - -/** - * GST_NTP_PACKET_SIZE: - * - * The size of the packets sent between NTP clocks. - */ -#define GST_NTP_PACKET_SIZE 48 - -typedef struct _GstNtpPacket GstNtpPacket; - -/** - * GstNtpPacket: - * @origin_time: the time the client packet was sent for the server - * @receive_time: the time the client packet was received - * @transmit_time: the time the packet was sent - * @poll_interval: maximum poll interval - * - * Content of a #GstNtpPacket. - */ -struct _GstNtpPacket { - GstClockTime origin_time; - GstClockTime receive_time; - GstClockTime transmit_time; - - GstClockTime poll_interval; -}; - -GType gst_ntp_packet_get_type(void) G_GNUC_INTERNAL; - -enum { - GST_NTP_ERROR_WRONG_VERSION, - GST_NTP_ERROR_KOD_DENY, - GST_NTP_ERROR_KOD_RATE, - GST_NTP_ERROR_KOD_UNKNOWN -}; - -GQuark gst_ntp_error_quark (void) G_GNUC_INTERNAL; -#define GST_NTP_ERROR (gst_ntp_error_quark ()) - -GstNtpPacket* gst_ntp_packet_new (const guint8 *buffer, - GError ** error) G_GNUC_INTERNAL; -GstNtpPacket* gst_ntp_packet_copy (const GstNtpPacket *packet) G_GNUC_INTERNAL; -void gst_ntp_packet_free (GstNtpPacket *packet) G_GNUC_INTERNAL; - -guint8* gst_ntp_packet_serialize (const GstNtpPacket *packet) G_GNUC_INTERNAL; - -GstNtpPacket* gst_ntp_packet_receive (GSocket * socket, - GSocketAddress ** src_address, - GError ** error) G_GNUC_INTERNAL; - -gboolean gst_ntp_packet_send (const GstNtpPacket * packet, - GSocket * socket, - GSocketAddress * dest_address, - GError ** error) G_GNUC_INTERNAL; - -G_END_DECLS - -#endif /* __GST_NET_TIME_PACKET_H__ */ diff --git a/libs/gst/net/gstptp_private.h b/libs/gst/net/gstptp_private.h deleted file mode 100644 index 18e0e07b35..0000000000 --- a/libs/gst/net/gstptp_private.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef __GST_PTP_PRIVATE_H__ -#define __GST_PTP_PRIVATE_H__ - -#include <glib.h> - -enum -{ - TYPE_EVENT, - TYPE_GENERAL, - TYPE_CLOCK_ID -}; - -typedef struct -{ - guint16 size; - guint8 type; -} StdIOHeader; - -#endif /* __GST_PTP_PRIVATE_H__ */ diff --git a/libs/gst/net/gstptpclock.c b/libs/gst/net/gstptpclock.c deleted file mode 100644 index 19b90e5969..0000000000 --- a/libs/gst/net/gstptpclock.c +++ /dev/null @@ -1,2660 +0,0 @@ -/* GStreamer - * Copyright (C) 2015 Sebastian Dröge <sebastian@centricular.com> - * - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ -/** - * SECTION:gstptpclock - * @title: GstPtpClock - * @short_description: Special clock that synchronizes to a remote time - * provider via PTP (IEEE1588:2008). - * @see_also: #GstClock, #GstNetClientClock, #GstPipeline - * - * GstPtpClock implements a PTP (IEEE1588:2008) ordinary clock in slave-only - * mode, that allows a GStreamer pipeline to synchronize to a PTP network - * clock in some specific domain. - * - * The PTP subsystem can be initialized with gst_ptp_init(), which then starts - * a helper process to do the actual communication via the PTP ports. This is - * required as PTP listens on ports < 1024 and thus requires special - * privileges. Once this helper process is started, the main process will - * synchronize to all PTP domains that are detected on the selected - * interfaces. - * - * gst_ptp_clock_new() then allows to create a GstClock that provides the PTP - * time from a master clock inside a specific PTP domain. This clock will only - * return valid timestamps once the timestamps in the PTP domain are known. To - * check this, you can use gst_clock_wait_for_sync(), the GstClock::synced - * signal and gst_clock_is_synced(). - * - * To gather statistics about the PTP clock synchronization, - * gst_ptp_statistics_callback_add() can be used. This gives the application - * the possibility to collect all kinds of statistics from the clock - * synchronization. - * - * Since: 1.6 - * - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstptpclock.h" - -#include "gstptp_private.h" - -#ifdef HAVE_SYS_WAIT_H -#include <sys/wait.h> -#endif -#ifdef G_OS_WIN32 -#define WIN32_LEAN_AND_MEAN -#include <windows.h> -#endif -#include <sys/types.h> - -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#elif defined(G_OS_WIN32) -#include <io.h> -#endif - -#include <gst/base/base.h> - -GST_DEBUG_CATEGORY_STATIC (ptp_debug); -#define GST_CAT_DEFAULT (ptp_debug) - -/* IEEE 1588 7.7.3.1 */ -#define PTP_ANNOUNCE_RECEIPT_TIMEOUT 4 - -/* Use a running average for calculating the mean path delay instead - * of just using the last measurement. Enabling this helps in unreliable - * networks, like wifi, with often changing delays - * - * Undef for following IEEE1588-2008 by the letter - */ -#define USE_RUNNING_AVERAGE_DELAY 1 - -/* Filter out any measurements that are above a certain threshold compared to - * previous measurements. Enabling this helps filtering out outliers that - * happen fairly often in unreliable networks, like wifi. - * - * Undef for following IEEE1588-2008 by the letter - */ -#define USE_MEASUREMENT_FILTERING 1 - -/* Select the first clock from which we capture a SYNC message as the master - * clock of the domain until we are ready to run the best master clock - * algorithm. This allows faster syncing but might mean a change of the master - * clock in the beginning. As all clocks in a domain are supposed to use the - * same time, this shouldn't be much of a problem. - * - * Undef for following IEEE1588-2008 by the letter - */ -#define USE_OPPORTUNISTIC_CLOCK_SELECTION 1 - -/* Only consider SYNC messages for which we are allowed to send a DELAY_REQ - * afterwards. This allows better synchronization in networks with varying - * delays, as for every other SYNC message we would have to assume that it's - * the average of what we saw before. But that might be completely off - */ -#define USE_ONLY_SYNC_WITH_DELAY 1 - -/* Filter out delay measurements that are too far away from the median of the - * last delay measurements, currently those that are more than 2 times as big. - * This increases accuracy a lot on wifi. - */ -#define USE_MEDIAN_PRE_FILTERING 1 -#define MEDIAN_PRE_FILTERING_WINDOW 9 - -/* How many updates should be skipped at maximum when using USE_MEASUREMENT_FILTERING */ -#define MAX_SKIPPED_UPDATES 5 - -typedef enum -{ - PTP_MESSAGE_TYPE_SYNC = 0x0, - PTP_MESSAGE_TYPE_DELAY_REQ = 0x1, - PTP_MESSAGE_TYPE_PDELAY_REQ = 0x2, - PTP_MESSAGE_TYPE_PDELAY_RESP = 0x3, - PTP_MESSAGE_TYPE_FOLLOW_UP = 0x8, - PTP_MESSAGE_TYPE_DELAY_RESP = 0x9, - PTP_MESSAGE_TYPE_PDELAY_RESP_FOLLOW_UP = 0xA, - PTP_MESSAGE_TYPE_ANNOUNCE = 0xB, - PTP_MESSAGE_TYPE_SIGNALING = 0xC, - PTP_MESSAGE_TYPE_MANAGEMENT = 0xD -} PtpMessageType; - -typedef struct -{ - guint64 seconds_field; /* 48 bits valid */ - guint32 nanoseconds_field; -} PtpTimestamp; - -#define PTP_TIMESTAMP_TO_GST_CLOCK_TIME(ptp) (ptp.seconds_field * GST_SECOND + ptp.nanoseconds_field) -#define GST_CLOCK_TIME_TO_PTP_TIMESTAMP_SECONDS(gst) (((GstClockTime) gst) / GST_SECOND) -#define GST_CLOCK_TIME_TO_PTP_TIMESTAMP_NANOSECONDS(gst) (((GstClockTime) gst) % GST_SECOND) - -typedef struct -{ - guint64 clock_identity; - guint16 port_number; -} PtpClockIdentity; - -static gint -compare_clock_identity (const PtpClockIdentity * a, const PtpClockIdentity * b) -{ - if (a->clock_identity < b->clock_identity) - return -1; - else if (a->clock_identity > b->clock_identity) - return 1; - - if (a->port_number < b->port_number) - return -1; - else if (a->port_number > b->port_number) - return 1; - - return 0; -} - -typedef struct -{ - guint8 clock_class; - guint8 clock_accuracy; - guint16 offset_scaled_log_variance; -} PtpClockQuality; - -typedef struct -{ - guint8 transport_specific; - PtpMessageType message_type; - /* guint8 reserved; */ - guint8 version_ptp; - guint16 message_length; - guint8 domain_number; - /* guint8 reserved; */ - guint16 flag_field; - gint64 correction_field; /* 48.16 fixed point nanoseconds */ - /* guint32 reserved; */ - PtpClockIdentity source_port_identity; - guint16 sequence_id; - guint8 control_field; - gint8 log_message_interval; - - union - { - struct - { - PtpTimestamp origin_timestamp; - gint16 current_utc_offset; - /* guint8 reserved; */ - guint8 grandmaster_priority_1; - PtpClockQuality grandmaster_clock_quality; - guint8 grandmaster_priority_2; - guint64 grandmaster_identity; - guint16 steps_removed; - guint8 time_source; - } announce; - - struct - { - PtpTimestamp origin_timestamp; - } sync; - - struct - { - PtpTimestamp precise_origin_timestamp; - } follow_up; - - struct - { - PtpTimestamp origin_timestamp; - } delay_req; - - struct - { - PtpTimestamp receive_timestamp; - PtpClockIdentity requesting_port_identity; - } delay_resp; - - } message_specific; -} PtpMessage; - -static GMutex ptp_lock; -static GCond ptp_cond; -static gboolean initted = FALSE; -#ifdef HAVE_PTP -static gboolean supported = TRUE; -#else -static gboolean supported = FALSE; -#endif -static GPid ptp_helper_pid; -static GThread *ptp_helper_thread; -static GMainContext *main_context; -static GMainLoop *main_loop; -static GIOChannel *stdin_channel, *stdout_channel; -static GRand *delay_req_rand; -static GstClock *observation_system_clock; -static PtpClockIdentity ptp_clock_id = { GST_PTP_CLOCK_ID_NONE, 0 }; - -typedef struct -{ - GstClockTime receive_time; - - PtpClockIdentity master_clock_identity; - - guint8 grandmaster_priority_1; - PtpClockQuality grandmaster_clock_quality; - guint8 grandmaster_priority_2; - guint64 grandmaster_identity; - guint16 steps_removed; - guint8 time_source; - - guint16 sequence_id; -} PtpAnnounceMessage; - -typedef struct -{ - PtpClockIdentity master_clock_identity; - - GstClockTime announce_interval; /* last interval we received */ - GQueue announce_messages; -} PtpAnnounceSender; - -typedef struct -{ - guint domain; - PtpClockIdentity master_clock_identity; - - guint16 sync_seqnum; - GstClockTime sync_recv_time_local; /* t2 */ - GstClockTime sync_send_time_remote; /* t1, might be -1 if FOLLOW_UP pending */ - GstClockTime follow_up_recv_time_local; - - GSource *timeout_source; - guint16 delay_req_seqnum; - GstClockTime delay_req_send_time_local; /* t3, -1 if we wait for FOLLOW_UP */ - GstClockTime delay_req_recv_time_remote; /* t4, -1 if we wait */ - GstClockTime delay_resp_recv_time_local; - - gint64 correction_field_sync; /* sum of the correction fields of SYNC/FOLLOW_UP */ - gint64 correction_field_delay; /* sum of the correction fields of DELAY_RESP */ -} PtpPendingSync; - -static void -ptp_pending_sync_free (PtpPendingSync * sync) -{ - if (sync->timeout_source) { - g_source_destroy (sync->timeout_source); - g_source_unref (sync->timeout_source); - } - g_free (sync); -} - -typedef struct -{ - guint domain; - - GstClockTime last_ptp_time; - GstClockTime last_local_time; - gint skipped_updates; - - /* Used for selecting the master/grandmaster */ - GList *announce_senders; - - /* Last selected master clock */ - gboolean have_master_clock; - PtpClockIdentity master_clock_identity; - guint64 grandmaster_identity; - - /* Last SYNC or FOLLOW_UP timestamp we received */ - GstClockTime last_ptp_sync_time; - GstClockTime sync_interval; - - GstClockTime mean_path_delay; - GstClockTime last_delay_req, min_delay_req_interval; - guint16 last_delay_req_seqnum; - - GstClockTime last_path_delays[MEDIAN_PRE_FILTERING_WINDOW]; - gint last_path_delays_missing; - - GQueue pending_syncs; - - GstClock *domain_clock; -} PtpDomainData; - -static GList *domain_data; -static GMutex domain_clocks_lock; -static GList *domain_clocks; - -/* Protected by PTP lock */ -static void emit_ptp_statistics (guint8 domain, const GstStructure * stats); -static GHookList domain_stats_hooks; -static gint domain_stats_n_hooks; -static gboolean domain_stats_hooks_initted = FALSE; - -/* Converts log2 seconds to GstClockTime */ -static GstClockTime -log2_to_clock_time (gint l) -{ - if (l < 0) - return GST_SECOND >> (-l); - else - return GST_SECOND << l; -} - -static void -dump_ptp_message (PtpMessage * msg) -{ - GST_TRACE ("PTP message:"); - GST_TRACE ("\ttransport_specific: %u", msg->transport_specific); - GST_TRACE ("\tmessage_type: 0x%01x", msg->message_type); - GST_TRACE ("\tversion_ptp: %u", msg->version_ptp); - GST_TRACE ("\tmessage_length: %u", msg->message_length); - GST_TRACE ("\tdomain_number: %u", msg->domain_number); - GST_TRACE ("\tflag_field: 0x%04x", msg->flag_field); - GST_TRACE ("\tcorrection_field: %" G_GINT64_FORMAT ".%03u", - (msg->correction_field / 65536), - (guint) ((msg->correction_field & 0xffff) * 1000) / 65536); - GST_TRACE ("\tsource_port_identity: 0x%016" G_GINT64_MODIFIER "x %u", - msg->source_port_identity.clock_identity, - msg->source_port_identity.port_number); - GST_TRACE ("\tsequence_id: %u", msg->sequence_id); - GST_TRACE ("\tcontrol_field: 0x%02x", msg->control_field); - GST_TRACE ("\tmessage_interval: %" GST_TIME_FORMAT, - GST_TIME_ARGS (log2_to_clock_time (msg->log_message_interval))); - - switch (msg->message_type) { - case PTP_MESSAGE_TYPE_ANNOUNCE: - GST_TRACE ("\tANNOUNCE:"); - GST_TRACE ("\t\torigin_timestamp: %" G_GUINT64_FORMAT ".%09u", - msg->message_specific.announce.origin_timestamp.seconds_field, - msg->message_specific.announce.origin_timestamp.nanoseconds_field); - GST_TRACE ("\t\tcurrent_utc_offset: %d", - msg->message_specific.announce.current_utc_offset); - GST_TRACE ("\t\tgrandmaster_priority_1: %u", - msg->message_specific.announce.grandmaster_priority_1); - GST_TRACE ("\t\tgrandmaster_clock_quality: 0x%02x 0x%02x %u", - msg->message_specific.announce.grandmaster_clock_quality.clock_class, - msg->message_specific.announce. - grandmaster_clock_quality.clock_accuracy, - msg->message_specific.announce. - grandmaster_clock_quality.offset_scaled_log_variance); - GST_TRACE ("\t\tgrandmaster_priority_2: %u", - msg->message_specific.announce.grandmaster_priority_2); - GST_TRACE ("\t\tgrandmaster_identity: 0x%016" G_GINT64_MODIFIER "x", - msg->message_specific.announce.grandmaster_identity); - GST_TRACE ("\t\tsteps_removed: %u", - msg->message_specific.announce.steps_removed); - GST_TRACE ("\t\ttime_source: 0x%02x", - msg->message_specific.announce.time_source); - break; - case PTP_MESSAGE_TYPE_SYNC: - GST_TRACE ("\tSYNC:"); - GST_TRACE ("\t\torigin_timestamp: %" G_GUINT64_FORMAT ".%09u", - msg->message_specific.sync.origin_timestamp.seconds_field, - msg->message_specific.sync.origin_timestamp.nanoseconds_field); - break; - case PTP_MESSAGE_TYPE_FOLLOW_UP: - GST_TRACE ("\tFOLLOW_UP:"); - GST_TRACE ("\t\tprecise_origin_timestamp: %" G_GUINT64_FORMAT ".%09u", - msg->message_specific.follow_up. - precise_origin_timestamp.seconds_field, - msg->message_specific.follow_up. - precise_origin_timestamp.nanoseconds_field); - break; - case PTP_MESSAGE_TYPE_DELAY_REQ: - GST_TRACE ("\tDELAY_REQ:"); - GST_TRACE ("\t\torigin_timestamp: %" G_GUINT64_FORMAT ".%09u", - msg->message_specific.delay_req.origin_timestamp.seconds_field, - msg->message_specific.delay_req.origin_timestamp.nanoseconds_field); - break; - case PTP_MESSAGE_TYPE_DELAY_RESP: - GST_TRACE ("\tDELAY_RESP:"); - GST_TRACE ("\t\treceive_timestamp: %" G_GUINT64_FORMAT ".%09u", - msg->message_specific.delay_resp.receive_timestamp.seconds_field, - msg->message_specific.delay_resp.receive_timestamp.nanoseconds_field); - GST_TRACE ("\t\trequesting_port_identity: 0x%016" G_GINT64_MODIFIER - "x %u", - msg->message_specific.delay_resp. - requesting_port_identity.clock_identity, - msg->message_specific.delay_resp. - requesting_port_identity.port_number); - break; - default: - break; - } - GST_TRACE (" "); -} - -/* IEEE 1588-2008 5.3.3 */ -static gboolean -parse_ptp_timestamp (PtpTimestamp * timestamp, GstByteReader * reader) -{ - g_return_val_if_fail (gst_byte_reader_get_remaining (reader) >= 10, FALSE); - - timestamp->seconds_field = - (((guint64) gst_byte_reader_get_uint32_be_unchecked (reader)) << 16) | - gst_byte_reader_get_uint16_be_unchecked (reader); - timestamp->nanoseconds_field = - gst_byte_reader_get_uint32_be_unchecked (reader); - - if (timestamp->nanoseconds_field >= 1000000000) - return FALSE; - - return TRUE; -} - -/* IEEE 1588-2008 13.3 */ -static gboolean -parse_ptp_message_header (PtpMessage * msg, GstByteReader * reader) -{ - guint8 b; - - g_return_val_if_fail (gst_byte_reader_get_remaining (reader) >= 34, FALSE); - - b = gst_byte_reader_get_uint8_unchecked (reader); - msg->transport_specific = b >> 4; - msg->message_type = b & 0x0f; - - b = gst_byte_reader_get_uint8_unchecked (reader); - msg->version_ptp = b & 0x0f; - if (msg->version_ptp != 2) { - GST_WARNING ("Unsupported PTP message version (%u != 2)", msg->version_ptp); - return FALSE; - } - - msg->message_length = gst_byte_reader_get_uint16_be_unchecked (reader); - if (gst_byte_reader_get_remaining (reader) + 4 < msg->message_length) { - GST_WARNING ("Not enough data (%u < %u)", - gst_byte_reader_get_remaining (reader) + 4, msg->message_length); - return FALSE; - } - - msg->domain_number = gst_byte_reader_get_uint8_unchecked (reader); - gst_byte_reader_skip_unchecked (reader, 1); - - msg->flag_field = gst_byte_reader_get_uint16_be_unchecked (reader); - msg->correction_field = gst_byte_reader_get_uint64_be_unchecked (reader); - gst_byte_reader_skip_unchecked (reader, 4); - - msg->source_port_identity.clock_identity = - gst_byte_reader_get_uint64_be_unchecked (reader); - msg->source_port_identity.port_number = - gst_byte_reader_get_uint16_be_unchecked (reader); - - msg->sequence_id = gst_byte_reader_get_uint16_be_unchecked (reader); - msg->control_field = gst_byte_reader_get_uint8_unchecked (reader); - msg->log_message_interval = gst_byte_reader_get_uint8_unchecked (reader); - - return TRUE; -} - -/* IEEE 1588-2008 13.5 */ -static gboolean -parse_ptp_message_announce (PtpMessage * msg, GstByteReader * reader) -{ - g_return_val_if_fail (msg->message_type == PTP_MESSAGE_TYPE_ANNOUNCE, FALSE); - - if (gst_byte_reader_get_remaining (reader) < 20) - return FALSE; - - if (!parse_ptp_timestamp (&msg->message_specific.announce.origin_timestamp, - reader)) - return FALSE; - - msg->message_specific.announce.current_utc_offset = - gst_byte_reader_get_uint16_be_unchecked (reader); - gst_byte_reader_skip_unchecked (reader, 1); - - msg->message_specific.announce.grandmaster_priority_1 = - gst_byte_reader_get_uint8_unchecked (reader); - msg->message_specific.announce.grandmaster_clock_quality.clock_class = - gst_byte_reader_get_uint8_unchecked (reader); - msg->message_specific.announce.grandmaster_clock_quality.clock_accuracy = - gst_byte_reader_get_uint8_unchecked (reader); - msg->message_specific.announce. - grandmaster_clock_quality.offset_scaled_log_variance = - gst_byte_reader_get_uint16_be_unchecked (reader); - msg->message_specific.announce.grandmaster_priority_2 = - gst_byte_reader_get_uint8_unchecked (reader); - msg->message_specific.announce.grandmaster_identity = - gst_byte_reader_get_uint64_be_unchecked (reader); - msg->message_specific.announce.steps_removed = - gst_byte_reader_get_uint16_be_unchecked (reader); - msg->message_specific.announce.time_source = - gst_byte_reader_get_uint8_unchecked (reader); - - return TRUE; -} - -/* IEEE 1588-2008 13.6 */ -static gboolean -parse_ptp_message_sync (PtpMessage * msg, GstByteReader * reader) -{ - g_return_val_if_fail (msg->message_type == PTP_MESSAGE_TYPE_SYNC, FALSE); - - if (gst_byte_reader_get_remaining (reader) < 10) - return FALSE; - - if (!parse_ptp_timestamp (&msg->message_specific.sync.origin_timestamp, - reader)) - return FALSE; - - return TRUE; -} - -/* IEEE 1588-2008 13.6 */ -static gboolean -parse_ptp_message_delay_req (PtpMessage * msg, GstByteReader * reader) -{ - g_return_val_if_fail (msg->message_type == PTP_MESSAGE_TYPE_DELAY_REQ, FALSE); - - if (gst_byte_reader_get_remaining (reader) < 10) - return FALSE; - - if (!parse_ptp_timestamp (&msg->message_specific.delay_req.origin_timestamp, - reader)) - return FALSE; - - return TRUE; -} - -/* IEEE 1588-2008 13.7 */ -static gboolean -parse_ptp_message_follow_up (PtpMessage * msg, GstByteReader * reader) -{ - g_return_val_if_fail (msg->message_type == PTP_MESSAGE_TYPE_FOLLOW_UP, FALSE); - - if (gst_byte_reader_get_remaining (reader) < 10) - return FALSE; - - if (!parse_ptp_timestamp (&msg->message_specific. - follow_up.precise_origin_timestamp, reader)) - return FALSE; - - return TRUE; -} - -/* IEEE 1588-2008 13.8 */ -static gboolean -parse_ptp_message_delay_resp (PtpMessage * msg, GstByteReader * reader) -{ - g_return_val_if_fail (msg->message_type == PTP_MESSAGE_TYPE_DELAY_RESP, - FALSE); - - if (gst_byte_reader_get_remaining (reader) < 20) - return FALSE; - - if (!parse_ptp_timestamp (&msg->message_specific.delay_resp.receive_timestamp, - reader)) - return FALSE; - - msg->message_specific.delay_resp.requesting_port_identity.clock_identity = - gst_byte_reader_get_uint64_be_unchecked (reader); - msg->message_specific.delay_resp.requesting_port_identity.port_number = - gst_byte_reader_get_uint16_be_unchecked (reader); - - return TRUE; -} - -static gboolean -parse_ptp_message (PtpMessage * msg, const guint8 * data, gsize size) -{ - GstByteReader reader; - gboolean ret = FALSE; - - gst_byte_reader_init (&reader, data, size); - - if (!parse_ptp_message_header (msg, &reader)) { - GST_WARNING ("Failed to parse PTP message header"); - return FALSE; - } - - switch (msg->message_type) { - case PTP_MESSAGE_TYPE_SYNC: - ret = parse_ptp_message_sync (msg, &reader); - break; - case PTP_MESSAGE_TYPE_FOLLOW_UP: - ret = parse_ptp_message_follow_up (msg, &reader); - break; - case PTP_MESSAGE_TYPE_DELAY_REQ: - ret = parse_ptp_message_delay_req (msg, &reader); - break; - case PTP_MESSAGE_TYPE_DELAY_RESP: - ret = parse_ptp_message_delay_resp (msg, &reader); - break; - case PTP_MESSAGE_TYPE_ANNOUNCE: - ret = parse_ptp_message_announce (msg, &reader); - break; - default: - /* ignore for now */ - break; - } - - return ret; -} - -static gint -compare_announce_message (const PtpAnnounceMessage * a, - const PtpAnnounceMessage * b) -{ - /* IEEE 1588 Figure 27 */ - if (a->grandmaster_identity == b->grandmaster_identity) { - if (a->steps_removed + 1 < b->steps_removed) - return -1; - else if (a->steps_removed > b->steps_removed + 1) - return 1; - - /* Error cases are filtered out earlier */ - if (a->steps_removed < b->steps_removed) - return -1; - else if (a->steps_removed > b->steps_removed) - return 1; - - /* Error cases are filtered out earlier */ - if (a->master_clock_identity.clock_identity < - b->master_clock_identity.clock_identity) - return -1; - else if (a->master_clock_identity.clock_identity > - b->master_clock_identity.clock_identity) - return 1; - - /* Error cases are filtered out earlier */ - if (a->master_clock_identity.port_number < - b->master_clock_identity.port_number) - return -1; - else if (a->master_clock_identity.port_number > - b->master_clock_identity.port_number) - return 1; - else - g_assert_not_reached (); - - return 0; - } - - if (a->grandmaster_priority_1 < b->grandmaster_priority_1) - return -1; - else if (a->grandmaster_priority_1 > b->grandmaster_priority_1) - return 1; - - if (a->grandmaster_clock_quality.clock_class < - b->grandmaster_clock_quality.clock_class) - return -1; - else if (a->grandmaster_clock_quality.clock_class > - b->grandmaster_clock_quality.clock_class) - return 1; - - if (a->grandmaster_clock_quality.clock_accuracy < - b->grandmaster_clock_quality.clock_accuracy) - return -1; - else if (a->grandmaster_clock_quality.clock_accuracy > - b->grandmaster_clock_quality.clock_accuracy) - return 1; - - if (a->grandmaster_clock_quality.offset_scaled_log_variance < - b->grandmaster_clock_quality.offset_scaled_log_variance) - return -1; - else if (a->grandmaster_clock_quality.offset_scaled_log_variance > - b->grandmaster_clock_quality.offset_scaled_log_variance) - return 1; - - if (a->grandmaster_priority_2 < b->grandmaster_priority_2) - return -1; - else if (a->grandmaster_priority_2 > b->grandmaster_priority_2) - return 1; - - if (a->grandmaster_identity < b->grandmaster_identity) - return -1; - else if (a->grandmaster_identity > b->grandmaster_identity) - return 1; - else - g_assert_not_reached (); - - return 0; -} - -static void -select_best_master_clock (PtpDomainData * domain, GstClockTime now) -{ - GList *qualified_messages = NULL; - GList *l, *m; - PtpAnnounceMessage *best = NULL; - - /* IEEE 1588 9.3.2.5 */ - for (l = domain->announce_senders; l; l = l->next) { - PtpAnnounceSender *sender = l->data; - GstClockTime window = 4 * sender->announce_interval; - gint count = 0; - - for (m = sender->announce_messages.head; m; m = m->next) { - PtpAnnounceMessage *msg = m->data; - - if (now - msg->receive_time <= window) - count++; - } - - /* Only include the newest message of announce senders that had at least 2 - * announce messages in the last 4 announce intervals. Which also means - * that we wait at least 4 announce intervals before we select a master - * clock. Until then we just report based on the newest SYNC we received - */ - if (count >= 2) { - qualified_messages = - g_list_prepend (qualified_messages, - g_queue_peek_tail (&sender->announce_messages)); - } - } - - if (!qualified_messages) { - GST_DEBUG - ("No qualified announce messages for domain %u, can't select a master clock", - domain->domain); - domain->have_master_clock = FALSE; - return; - } - - for (l = qualified_messages; l; l = l->next) { - PtpAnnounceMessage *msg = l->data; - - if (!best || compare_announce_message (msg, best) < 0) - best = msg; - } - g_clear_pointer (&qualified_messages, g_list_free); - - if (domain->have_master_clock - && compare_clock_identity (&domain->master_clock_identity, - &best->master_clock_identity) == 0) { - GST_DEBUG ("Master clock in domain %u did not change", domain->domain); - } else { - GST_DEBUG ("Selected master clock for domain %u: 0x%016" G_GINT64_MODIFIER - "x %u with grandmaster clock 0x%016" G_GINT64_MODIFIER "x", - domain->domain, best->master_clock_identity.clock_identity, - best->master_clock_identity.port_number, best->grandmaster_identity); - - domain->have_master_clock = TRUE; - domain->grandmaster_identity = best->grandmaster_identity; - - /* Opportunistic master clock selection likely gave us the same master - * clock before, no need to reset all statistics */ - if (compare_clock_identity (&domain->master_clock_identity, - &best->master_clock_identity) != 0) { - memcpy (&domain->master_clock_identity, &best->master_clock_identity, - sizeof (PtpClockIdentity)); - domain->mean_path_delay = 0; - domain->last_delay_req = 0; - domain->last_path_delays_missing = 9; - domain->min_delay_req_interval = 0; - domain->sync_interval = 0; - domain->last_ptp_sync_time = 0; - domain->skipped_updates = 0; - g_queue_foreach (&domain->pending_syncs, (GFunc) ptp_pending_sync_free, - NULL); - g_queue_clear (&domain->pending_syncs); - } - - if (g_atomic_int_get (&domain_stats_n_hooks)) { - GstStructure *stats = - gst_structure_new (GST_PTP_STATISTICS_BEST_MASTER_CLOCK_SELECTED, - "domain", G_TYPE_UINT, domain->domain, - "master-clock-id", G_TYPE_UINT64, - domain->master_clock_identity.clock_identity, - "master-clock-port", G_TYPE_UINT, - domain->master_clock_identity.port_number, - "grandmaster-clock-id", G_TYPE_UINT64, domain->grandmaster_identity, - NULL); - emit_ptp_statistics (domain->domain, stats); - gst_structure_free (stats); - } - } -} - -static void -handle_announce_message (PtpMessage * msg, GstClockTime receive_time) -{ - GList *l; - PtpDomainData *domain = NULL; - PtpAnnounceSender *sender = NULL; - PtpAnnounceMessage *announce; - - /* IEEE1588 9.3.2.2 e) - * Don't consider messages with the alternate master flag set - */ - if ((msg->flag_field & 0x0100)) - return; - - /* IEEE 1588 9.3.2.5 d) - * Don't consider announce messages with steps_removed>=255 - */ - if (msg->message_specific.announce.steps_removed >= 255) - return; - - for (l = domain_data; l; l = l->next) { - PtpDomainData *tmp = l->data; - - if (tmp->domain == msg->domain_number) { - domain = tmp; - break; - } - } - - if (!domain) { - gchar *clock_name; - - domain = g_new0 (PtpDomainData, 1); - domain->domain = msg->domain_number; - clock_name = g_strdup_printf ("ptp-clock-%u", domain->domain); - domain->domain_clock = - g_object_new (GST_TYPE_SYSTEM_CLOCK, "name", clock_name, NULL); - gst_object_ref_sink (domain->domain_clock); - g_free (clock_name); - g_queue_init (&domain->pending_syncs); - domain->last_path_delays_missing = 9; - domain_data = g_list_prepend (domain_data, domain); - - g_mutex_lock (&domain_clocks_lock); - domain_clocks = g_list_prepend (domain_clocks, domain); - g_mutex_unlock (&domain_clocks_lock); - - if (g_atomic_int_get (&domain_stats_n_hooks)) { - GstStructure *stats = - gst_structure_new (GST_PTP_STATISTICS_NEW_DOMAIN_FOUND, "domain", - G_TYPE_UINT, domain->domain, "clock", GST_TYPE_CLOCK, - domain->domain_clock, NULL); - emit_ptp_statistics (domain->domain, stats); - gst_structure_free (stats); - } - } - - for (l = domain->announce_senders; l; l = l->next) { - PtpAnnounceSender *tmp = l->data; - - if (compare_clock_identity (&tmp->master_clock_identity, - &msg->source_port_identity) == 0) { - sender = tmp; - break; - } - } - - if (!sender) { - sender = g_new0 (PtpAnnounceSender, 1); - - memcpy (&sender->master_clock_identity, &msg->source_port_identity, - sizeof (PtpClockIdentity)); - g_queue_init (&sender->announce_messages); - domain->announce_senders = - g_list_prepend (domain->announce_senders, sender); - } - - for (l = sender->announce_messages.head; l; l = l->next) { - PtpAnnounceMessage *tmp = l->data; - - /* IEEE 1588 9.3.2.5 c) - * Don't consider identical messages, i.e. duplicates - */ - if (tmp->sequence_id == msg->sequence_id) - return; - } - - sender->announce_interval = log2_to_clock_time (msg->log_message_interval); - - announce = g_new0 (PtpAnnounceMessage, 1); - announce->receive_time = receive_time; - announce->sequence_id = msg->sequence_id; - memcpy (&announce->master_clock_identity, &msg->source_port_identity, - sizeof (PtpClockIdentity)); - announce->grandmaster_identity = - msg->message_specific.announce.grandmaster_identity; - announce->grandmaster_priority_1 = - msg->message_specific.announce.grandmaster_priority_1; - announce->grandmaster_clock_quality.clock_class = - msg->message_specific.announce.grandmaster_clock_quality.clock_class; - announce->grandmaster_clock_quality.clock_accuracy = - msg->message_specific.announce.grandmaster_clock_quality.clock_accuracy; - announce->grandmaster_clock_quality.offset_scaled_log_variance = - msg->message_specific.announce. - grandmaster_clock_quality.offset_scaled_log_variance; - announce->grandmaster_priority_2 = - msg->message_specific.announce.grandmaster_priority_2; - announce->steps_removed = msg->message_specific.announce.steps_removed; - announce->time_source = msg->message_specific.announce.time_source; - g_queue_push_tail (&sender->announce_messages, announce); - - select_best_master_clock (domain, receive_time); -} - -static gboolean -send_delay_req_timeout (PtpPendingSync * sync) -{ - StdIOHeader header = { 0, }; - guint8 delay_req[44]; - GstByteWriter writer; - GIOStatus status; - gsize written; - GError *err = NULL; - - header.type = TYPE_EVENT; - header.size = 44; - - GST_TRACE ("Sending delay_req to domain %u", sync->domain); - - gst_byte_writer_init_with_data (&writer, delay_req, 44, FALSE); - gst_byte_writer_put_uint8_unchecked (&writer, PTP_MESSAGE_TYPE_DELAY_REQ); - gst_byte_writer_put_uint8_unchecked (&writer, 2); - gst_byte_writer_put_uint16_be_unchecked (&writer, 44); - gst_byte_writer_put_uint8_unchecked (&writer, sync->domain); - gst_byte_writer_put_uint8_unchecked (&writer, 0); - gst_byte_writer_put_uint16_be_unchecked (&writer, 0); - gst_byte_writer_put_uint64_be_unchecked (&writer, 0); - gst_byte_writer_put_uint32_be_unchecked (&writer, 0); - gst_byte_writer_put_uint64_be_unchecked (&writer, - ptp_clock_id.clock_identity); - gst_byte_writer_put_uint16_be_unchecked (&writer, ptp_clock_id.port_number); - gst_byte_writer_put_uint16_be_unchecked (&writer, sync->delay_req_seqnum); - gst_byte_writer_put_uint8_unchecked (&writer, 0x01); - gst_byte_writer_put_uint8_unchecked (&writer, 0x7f); - gst_byte_writer_put_uint64_be_unchecked (&writer, 0); - gst_byte_writer_put_uint16_be_unchecked (&writer, 0); - - status = - g_io_channel_write_chars (stdout_channel, (gchar *) & header, - sizeof (header), &written, &err); - if (status == G_IO_STATUS_ERROR) { - g_warning ("Failed to write to stdout: %s", err->message); - g_clear_error (&err); - return G_SOURCE_REMOVE; - } else if (status == G_IO_STATUS_EOF) { - g_message ("EOF on stdout"); - g_main_loop_quit (main_loop); - return G_SOURCE_REMOVE; - } else if (status != G_IO_STATUS_NORMAL) { - g_warning ("Unexpected stdout write status: %d", status); - g_main_loop_quit (main_loop); - return G_SOURCE_REMOVE; - } else if (written != sizeof (header)) { - g_warning ("Unexpected write size: %" G_GSIZE_FORMAT, written); - g_main_loop_quit (main_loop); - return G_SOURCE_REMOVE; - } - - sync->delay_req_send_time_local = - gst_clock_get_time (observation_system_clock); - - status = - g_io_channel_write_chars (stdout_channel, - (const gchar *) delay_req, 44, &written, &err); - if (status == G_IO_STATUS_ERROR) { - g_warning ("Failed to write to stdout: %s", err->message); - g_clear_error (&err); - g_main_loop_quit (main_loop); - return G_SOURCE_REMOVE; - } else if (status == G_IO_STATUS_EOF) { - g_message ("EOF on stdout"); - g_main_loop_quit (main_loop); - return G_SOURCE_REMOVE; - } else if (status != G_IO_STATUS_NORMAL) { - g_warning ("Unexpected stdout write status: %d", status); - g_main_loop_quit (main_loop); - return G_SOURCE_REMOVE; - } else if (written != 44) { - g_warning ("Unexpected write size: %" G_GSIZE_FORMAT, written); - g_main_loop_quit (main_loop); - return G_SOURCE_REMOVE; - } - - return G_SOURCE_REMOVE; -} - -static gboolean -send_delay_req (PtpDomainData * domain, PtpPendingSync * sync) -{ - GstClockTime now = gst_clock_get_time (observation_system_clock); - guint timeout; - GSource *timeout_source; - - if (domain->last_delay_req != 0 - && domain->last_delay_req + domain->min_delay_req_interval > now) { - GST_TRACE ("Too soon to send new DELAY_REQ"); - return FALSE; - } - - domain->last_delay_req = now; - sync->delay_req_seqnum = domain->last_delay_req_seqnum++; - - /* IEEE 1588 9.5.11.2 */ - if (domain->last_delay_req == 0 || domain->min_delay_req_interval == 0) - timeout = 0; - else - timeout = - g_rand_int_range (delay_req_rand, 0, - (domain->min_delay_req_interval * 2) / GST_MSECOND); - - sync->timeout_source = timeout_source = g_timeout_source_new (timeout); - g_source_set_priority (timeout_source, G_PRIORITY_DEFAULT); - g_source_set_callback (timeout_source, (GSourceFunc) send_delay_req_timeout, - sync, NULL); - g_source_attach (timeout_source, main_context); - - return TRUE; -} - -/* Filtering of outliers for RTT and time calculations inspired - * by the code from gstnetclientclock.c - */ -static void -update_ptp_time (PtpDomainData * domain, PtpPendingSync * sync) -{ - GstClockTime internal_time, external_time, rate_num, rate_den; - GstClockTime corrected_ptp_time, corrected_local_time; - gdouble r_squared = 0.0; - gboolean synced; - GstClockTimeDiff discont = 0; - GstClockTime estimated_ptp_time = GST_CLOCK_TIME_NONE; -#ifdef USE_MEASUREMENT_FILTERING - GstClockTime orig_internal_time, orig_external_time, orig_rate_num, - orig_rate_den; - GstClockTime new_estimated_ptp_time; - GstClockTime max_discont, estimated_ptp_time_min, estimated_ptp_time_max; - gboolean now_synced; -#endif -#ifdef USE_ONLY_SYNC_WITH_DELAY - GstClockTime mean_path_delay; -#endif - - GST_TRACE ("Updating PTP time"); - -#ifdef USE_ONLY_SYNC_WITH_DELAY - if (sync->delay_req_send_time_local == GST_CLOCK_TIME_NONE) { - GST_TRACE ("Not updating - no delay_req sent"); - return; - } - - /* IEEE 1588 11.3 */ - mean_path_delay = - (sync->delay_req_recv_time_remote - sync->sync_send_time_remote + - sync->sync_recv_time_local - sync->delay_req_send_time_local - - (sync->correction_field_sync + sync->correction_field_delay + - 32768) / 65536) / 2; -#endif - - /* IEEE 1588 11.2 */ - corrected_ptp_time = - sync->sync_send_time_remote + - (sync->correction_field_sync + 32768) / 65536; - -#ifdef USE_ONLY_SYNC_WITH_DELAY - corrected_local_time = sync->sync_recv_time_local - mean_path_delay; -#else - corrected_local_time = sync->sync_recv_time_local - domain->mean_path_delay; -#endif - -#ifdef USE_MEASUREMENT_FILTERING - /* We check this here and when updating the mean path delay, because - * we can get here without a delay response too. The tolerance on - * accepting follow-up after a sync is high, because a PTP server - * doesn't have to prioritise sending FOLLOW_UP - its purpose is - * just to give us the accurate timestamp of the preceding SYNC */ - if (sync->follow_up_recv_time_local != GST_CLOCK_TIME_NONE - && sync->follow_up_recv_time_local > - sync->sync_recv_time_local + 20 * domain->mean_path_delay) { - GstClockTimeDiff delay = - sync->follow_up_recv_time_local - sync->sync_recv_time_local; - GST_WARNING ("Sync-follow-up delay for domain %u too big: %" - GST_STIME_FORMAT " > 20 * %" GST_TIME_FORMAT, domain->domain, - GST_STIME_ARGS (delay), GST_TIME_ARGS (domain->mean_path_delay)); - synced = FALSE; - gst_clock_get_calibration (GST_CLOCK_CAST (domain->domain_clock), - &internal_time, &external_time, &rate_num, &rate_den); - goto out; - } -#endif - - /* Set an initial local-remote relation */ - if (domain->last_ptp_time == 0) - gst_clock_set_calibration (domain->domain_clock, corrected_local_time, - corrected_ptp_time, 1, 1); - -#ifdef USE_MEASUREMENT_FILTERING - /* Check if the corrected PTP time is +/- 3/4 RTT around what we would - * estimate with our present knowledge about the clock - */ - /* Store what the clock produced as 'now' before this update */ - gst_clock_get_calibration (GST_CLOCK_CAST (domain->domain_clock), - &orig_internal_time, &orig_external_time, &orig_rate_num, &orig_rate_den); - internal_time = orig_internal_time; - external_time = orig_external_time; - rate_num = orig_rate_num; - rate_den = orig_rate_den; - - /* 3/4 RTT window around the estimation */ - max_discont = domain->mean_path_delay * 3 / 2; - - /* Check if the estimated sync time is inside our window */ - estimated_ptp_time_min = corrected_local_time - max_discont; - estimated_ptp_time_min = - gst_clock_adjust_with_calibration (GST_CLOCK_CAST (domain->domain_clock), - estimated_ptp_time_min, internal_time, external_time, rate_num, rate_den); - estimated_ptp_time_max = corrected_local_time + max_discont; - estimated_ptp_time_max = - gst_clock_adjust_with_calibration (GST_CLOCK_CAST (domain->domain_clock), - estimated_ptp_time_max, internal_time, external_time, rate_num, rate_den); - - synced = (estimated_ptp_time_min < corrected_ptp_time - && corrected_ptp_time < estimated_ptp_time_max); - - GST_DEBUG ("Adding observation for domain %u: %" GST_TIME_FORMAT " - %" - GST_TIME_FORMAT, domain->domain, - GST_TIME_ARGS (corrected_ptp_time), GST_TIME_ARGS (corrected_local_time)); - - GST_DEBUG ("Synced %d: %" GST_TIME_FORMAT " < %" GST_TIME_FORMAT " < %" - GST_TIME_FORMAT, synced, GST_TIME_ARGS (estimated_ptp_time_min), - GST_TIME_ARGS (corrected_ptp_time), - GST_TIME_ARGS (estimated_ptp_time_max)); - - if (gst_clock_add_observation_unapplied (domain->domain_clock, - corrected_local_time, corrected_ptp_time, &r_squared, - &internal_time, &external_time, &rate_num, &rate_den)) { - GST_DEBUG ("Regression gave r_squared: %f", r_squared); - - /* Old estimated PTP time based on receive time and path delay */ - estimated_ptp_time = corrected_local_time; - estimated_ptp_time = - gst_clock_adjust_with_calibration (GST_CLOCK_CAST - (domain->domain_clock), estimated_ptp_time, orig_internal_time, - orig_external_time, orig_rate_num, orig_rate_den); - - /* New estimated PTP time based on receive time and path delay */ - new_estimated_ptp_time = corrected_local_time; - new_estimated_ptp_time = - gst_clock_adjust_with_calibration (GST_CLOCK_CAST - (domain->domain_clock), new_estimated_ptp_time, internal_time, - external_time, rate_num, rate_den); - - discont = GST_CLOCK_DIFF (estimated_ptp_time, new_estimated_ptp_time); - if (synced && ABS (discont) > max_discont) { - GstClockTimeDiff offset; - GST_DEBUG ("Too large a discont %s%" GST_TIME_FORMAT - ", clamping to 1/4 average RTT = %" GST_TIME_FORMAT, - (discont < 0 ? "-" : ""), GST_TIME_ARGS (ABS (discont)), - GST_TIME_ARGS (max_discont)); - if (discont > 0) { /* Too large a forward step - add a -ve offset */ - offset = max_discont - discont; - if (-offset > external_time) - external_time = 0; - else - external_time += offset; - } else { /* Too large a backward step - add a +ve offset */ - offset = -(max_discont + discont); - external_time += offset; - } - - discont += offset; - } else { - GST_DEBUG ("Discont %s%" GST_TIME_FORMAT " (max: %" GST_TIME_FORMAT ")", - (discont < 0 ? "-" : ""), GST_TIME_ARGS (ABS (discont)), - GST_TIME_ARGS (max_discont)); - } - - /* Check if the estimated sync time is now (still) inside our window */ - estimated_ptp_time_min = corrected_local_time - max_discont; - estimated_ptp_time_min = - gst_clock_adjust_with_calibration (GST_CLOCK_CAST - (domain->domain_clock), estimated_ptp_time_min, internal_time, - external_time, rate_num, rate_den); - estimated_ptp_time_max = corrected_local_time + max_discont; - estimated_ptp_time_max = - gst_clock_adjust_with_calibration (GST_CLOCK_CAST - (domain->domain_clock), estimated_ptp_time_max, internal_time, - external_time, rate_num, rate_den); - - now_synced = (estimated_ptp_time_min < corrected_ptp_time - && corrected_ptp_time < estimated_ptp_time_max); - - GST_DEBUG ("Now synced %d: %" GST_TIME_FORMAT " < %" GST_TIME_FORMAT " < %" - GST_TIME_FORMAT, now_synced, GST_TIME_ARGS (estimated_ptp_time_min), - GST_TIME_ARGS (corrected_ptp_time), - GST_TIME_ARGS (estimated_ptp_time_max)); - - if (synced || now_synced || domain->skipped_updates > MAX_SKIPPED_UPDATES) { - gst_clock_set_calibration (GST_CLOCK_CAST (domain->domain_clock), - internal_time, external_time, rate_num, rate_den); - domain->skipped_updates = 0; - - domain->last_ptp_time = corrected_ptp_time; - domain->last_local_time = corrected_local_time; - } else { - domain->skipped_updates++; - } - } else { - domain->last_ptp_time = corrected_ptp_time; - domain->last_local_time = corrected_local_time; - } - -#else - GST_DEBUG ("Adding observation for domain %u: %" GST_TIME_FORMAT " - %" - GST_TIME_FORMAT, domain->domain, - GST_TIME_ARGS (corrected_ptp_time), GST_TIME_ARGS (corrected_local_time)); - - gst_clock_get_calibration (GST_CLOCK_CAST (domain->domain_clock), - &internal_time, &external_time, &rate_num, &rate_den); - - estimated_ptp_time = corrected_local_time; - estimated_ptp_time = - gst_clock_adjust_with_calibration (GST_CLOCK_CAST - (domain->domain_clock), estimated_ptp_time, internal_time, - external_time, rate_num, rate_den); - - gst_clock_add_observation (domain->domain_clock, - corrected_local_time, corrected_ptp_time, &r_squared); - - gst_clock_get_calibration (GST_CLOCK_CAST (domain->domain_clock), - &internal_time, &external_time, &rate_num, &rate_den); - - synced = TRUE; - domain->last_ptp_time = corrected_ptp_time; - domain->last_local_time = corrected_local_time; -#endif - -#ifdef USE_MEASUREMENT_FILTERING -out: -#endif - if (g_atomic_int_get (&domain_stats_n_hooks)) { - GstStructure *stats = gst_structure_new (GST_PTP_STATISTICS_TIME_UPDATED, - "domain", G_TYPE_UINT, domain->domain, - "mean-path-delay-avg", GST_TYPE_CLOCK_TIME, domain->mean_path_delay, - "local-time", GST_TYPE_CLOCK_TIME, corrected_local_time, - "ptp-time", GST_TYPE_CLOCK_TIME, corrected_ptp_time, - "estimated-ptp-time", GST_TYPE_CLOCK_TIME, estimated_ptp_time, - "discontinuity", G_TYPE_INT64, discont, - "synced", G_TYPE_BOOLEAN, synced, - "r-squared", G_TYPE_DOUBLE, r_squared, - "internal-time", GST_TYPE_CLOCK_TIME, internal_time, - "external-time", GST_TYPE_CLOCK_TIME, external_time, - "rate-num", G_TYPE_UINT64, rate_num, - "rate-den", G_TYPE_UINT64, rate_den, - "rate", G_TYPE_DOUBLE, (gdouble) (rate_num) / rate_den, - NULL); - emit_ptp_statistics (domain->domain, stats); - gst_structure_free (stats); - } - -} - -#ifdef USE_MEDIAN_PRE_FILTERING -static gint -compare_clock_time (const GstClockTime * a, const GstClockTime * b) -{ - if (*a < *b) - return -1; - else if (*a > *b) - return 1; - return 0; -} -#endif - -static gboolean -update_mean_path_delay (PtpDomainData * domain, PtpPendingSync * sync) -{ -#ifdef USE_MEDIAN_PRE_FILTERING - GstClockTime last_path_delays[MEDIAN_PRE_FILTERING_WINDOW]; - GstClockTime median; - gint i; -#endif - - GstClockTime mean_path_delay, delay_req_delay = 0; - gboolean ret; - - /* IEEE 1588 11.3 */ - mean_path_delay = - (sync->delay_req_recv_time_remote - sync->sync_send_time_remote + - sync->sync_recv_time_local - sync->delay_req_send_time_local - - (sync->correction_field_sync + sync->correction_field_delay + - 32768) / 65536) / 2; - -#ifdef USE_MEDIAN_PRE_FILTERING - for (i = 1; i < MEDIAN_PRE_FILTERING_WINDOW; i++) - domain->last_path_delays[i - 1] = domain->last_path_delays[i]; - domain->last_path_delays[i - 1] = mean_path_delay; - - if (domain->last_path_delays_missing) { - domain->last_path_delays_missing--; - } else { - memcpy (&last_path_delays, &domain->last_path_delays, - sizeof (last_path_delays)); - g_qsort_with_data (&last_path_delays, - MEDIAN_PRE_FILTERING_WINDOW, sizeof (GstClockTime), - (GCompareDataFunc) compare_clock_time, NULL); - - median = last_path_delays[MEDIAN_PRE_FILTERING_WINDOW / 2]; - - /* FIXME: We might want to use something else here, like only allowing - * things in the interquartile range, or also filtering away delays that - * are too small compared to the median. This here worked well enough - * in tests so far. - */ - if (mean_path_delay > 2 * median) { - GST_WARNING ("Path delay for domain %u too big compared to median: %" - GST_TIME_FORMAT " > 2 * %" GST_TIME_FORMAT, domain->domain, - GST_TIME_ARGS (mean_path_delay), GST_TIME_ARGS (median)); - ret = FALSE; - goto out; - } - } -#endif - -#ifdef USE_RUNNING_AVERAGE_DELAY - /* Track an average round trip time, for a bit of smoothing */ - /* Always update before discarding a sample, so genuine changes in - * the network get picked up, eventually */ - if (domain->mean_path_delay == 0) - domain->mean_path_delay = mean_path_delay; - else if (mean_path_delay < domain->mean_path_delay) /* Shorter RTTs carry more weight than longer */ - domain->mean_path_delay = - (3 * domain->mean_path_delay + mean_path_delay) / 4; - else - domain->mean_path_delay = - (15 * domain->mean_path_delay + mean_path_delay) / 16; -#else - domain->mean_path_delay = mean_path_delay; -#endif - -#ifdef USE_MEASUREMENT_FILTERING - /* The tolerance on accepting follow-up after a sync is high, because - * a PTP server doesn't have to prioritise sending FOLLOW_UP - its purpose is - * just to give us the accurate timestamp of the preceding SYNC */ - if (sync->follow_up_recv_time_local != GST_CLOCK_TIME_NONE && - domain->mean_path_delay != 0 - && sync->follow_up_recv_time_local > - sync->sync_recv_time_local + 20 * domain->mean_path_delay) { - GST_WARNING ("Sync-follow-up delay for domain %u too big: %" GST_TIME_FORMAT - " > 20 * %" GST_TIME_FORMAT, domain->domain, - GST_TIME_ARGS (sync->follow_up_recv_time_local - - sync->sync_recv_time_local), - GST_TIME_ARGS (domain->mean_path_delay)); - ret = FALSE; - goto out; - } - - if (mean_path_delay > 2 * domain->mean_path_delay) { - GST_WARNING ("Mean path delay for domain %u too big: %" GST_TIME_FORMAT - " > 2 * %" GST_TIME_FORMAT, domain->domain, - GST_TIME_ARGS (mean_path_delay), - GST_TIME_ARGS (domain->mean_path_delay)); - ret = FALSE; - goto out; - } -#endif - - delay_req_delay = - sync->delay_resp_recv_time_local - sync->delay_req_send_time_local; - -#ifdef USE_MEASUREMENT_FILTERING - /* delay_req_delay is a RTT, so 2 times the path delay is what we'd - * hope for, but some PTP systems don't prioritise sending DELAY_RESP, - * but they must still have placed an accurate reception timestamp. - * That means we should be quite tolerant about late DELAY_RESP, and - * mostly rely on filtering out jumps in the mean-path-delay elsewhere */ - if (delay_req_delay > 20 * domain->mean_path_delay) { - GST_WARNING ("Delay-request-response delay for domain %u too big: %" - GST_TIME_FORMAT " > 20 * %" GST_TIME_FORMAT, domain->domain, - GST_TIME_ARGS (delay_req_delay), - GST_TIME_ARGS (domain->mean_path_delay)); - ret = FALSE; - goto out; - } -#endif - - ret = TRUE; - - GST_DEBUG ("Got mean path delay for domain %u: %" GST_TIME_FORMAT " (new: %" - GST_TIME_FORMAT ")", domain->domain, - GST_TIME_ARGS (domain->mean_path_delay), GST_TIME_ARGS (mean_path_delay)); - GST_DEBUG ("Delay request delay for domain %u: %" GST_TIME_FORMAT, - domain->domain, GST_TIME_ARGS (delay_req_delay)); - -#if defined(USE_MEASUREMENT_FILTERING) || defined(USE_MEDIAN_PRE_FILTERING) -out: -#endif - if (g_atomic_int_get (&domain_stats_n_hooks)) { - GstStructure *stats = - gst_structure_new (GST_PTP_STATISTICS_PATH_DELAY_MEASURED, - "domain", G_TYPE_UINT, domain->domain, - "mean-path-delay-avg", GST_TYPE_CLOCK_TIME, domain->mean_path_delay, - "mean-path-delay", GST_TYPE_CLOCK_TIME, mean_path_delay, - "delay-request-delay", GST_TYPE_CLOCK_TIME, delay_req_delay, NULL); - emit_ptp_statistics (domain->domain, stats); - gst_structure_free (stats); - } - - return ret; -} - -static void -handle_sync_message (PtpMessage * msg, GstClockTime receive_time) -{ - GList *l; - PtpDomainData *domain = NULL; - PtpPendingSync *sync = NULL; - - /* Don't consider messages with the alternate master flag set */ - if ((msg->flag_field & 0x0100)) { - GST_TRACE ("Ignoring sync message with alternate-master flag"); - return; - } - - for (l = domain_data; l; l = l->next) { - PtpDomainData *tmp = l->data; - - if (msg->domain_number == tmp->domain) { - domain = tmp; - break; - } - } - - if (!domain) { - gchar *clock_name; - - domain = g_new0 (PtpDomainData, 1); - domain->domain = msg->domain_number; - clock_name = g_strdup_printf ("ptp-clock-%u", domain->domain); - domain->domain_clock = - g_object_new (GST_TYPE_SYSTEM_CLOCK, "name", clock_name, NULL); - gst_object_ref_sink (domain->domain_clock); - g_free (clock_name); - g_queue_init (&domain->pending_syncs); - domain->last_path_delays_missing = 9; - domain_data = g_list_prepend (domain_data, domain); - - g_mutex_lock (&domain_clocks_lock); - domain_clocks = g_list_prepend (domain_clocks, domain); - g_mutex_unlock (&domain_clocks_lock); - } - - /* If we have a master clock, ignore this message if it's not coming from there */ - if (domain->have_master_clock - && compare_clock_identity (&domain->master_clock_identity, - &msg->source_port_identity) != 0) - return; - -#ifdef USE_OPPORTUNISTIC_CLOCK_SELECTION - /* Opportunistic selection of master clock */ - if (!domain->have_master_clock) - memcpy (&domain->master_clock_identity, &msg->source_port_identity, - sizeof (PtpClockIdentity)); -#else - if (!domain->have_master_clock) - return; -#endif - - domain->sync_interval = log2_to_clock_time (msg->log_message_interval); - - /* Check if duplicated */ - for (l = domain->pending_syncs.head; l; l = l->next) { - PtpPendingSync *tmp = l->data; - - if (tmp->sync_seqnum == msg->sequence_id) - return; - } - - if (msg->message_specific.sync.origin_timestamp.seconds_field > - GST_CLOCK_TIME_NONE / GST_SECOND) { - GST_FIXME ("Unsupported sync message seconds field value: %" - G_GUINT64_FORMAT " > %" G_GUINT64_FORMAT, - msg->message_specific.sync.origin_timestamp.seconds_field, - GST_CLOCK_TIME_NONE / GST_SECOND); - return; - } - - sync = g_new0 (PtpPendingSync, 1); - sync->domain = domain->domain; - sync->sync_seqnum = msg->sequence_id; - sync->sync_recv_time_local = receive_time; - sync->sync_send_time_remote = GST_CLOCK_TIME_NONE; - sync->follow_up_recv_time_local = GST_CLOCK_TIME_NONE; - sync->delay_req_send_time_local = GST_CLOCK_TIME_NONE; - sync->delay_req_recv_time_remote = GST_CLOCK_TIME_NONE; - sync->delay_resp_recv_time_local = GST_CLOCK_TIME_NONE; - - /* 0.5 correction factor for division later */ - sync->correction_field_sync = msg->correction_field; - - if ((msg->flag_field & 0x0200)) { - /* Wait for FOLLOW_UP */ - GST_TRACE ("Waiting for FOLLOW_UP msg"); - } else { - sync->sync_send_time_remote = - PTP_TIMESTAMP_TO_GST_CLOCK_TIME (msg->message_specific. - sync.origin_timestamp); - - if (domain->last_ptp_sync_time != 0 - && domain->last_ptp_sync_time >= sync->sync_send_time_remote) { - GST_WARNING ("Backwards PTP times in domain %u: %" GST_TIME_FORMAT " >= %" - GST_TIME_FORMAT, domain->domain, - GST_TIME_ARGS (domain->last_ptp_sync_time), - GST_TIME_ARGS (sync->sync_send_time_remote)); - ptp_pending_sync_free (sync); - sync = NULL; - return; - } - domain->last_ptp_sync_time = sync->sync_send_time_remote; - - if (send_delay_req (domain, sync)) { - /* Sent delay request */ - } else { - update_ptp_time (domain, sync); - ptp_pending_sync_free (sync); - sync = NULL; - } - } - - if (sync) - g_queue_push_tail (&domain->pending_syncs, sync); -} - -static void -handle_follow_up_message (PtpMessage * msg, GstClockTime receive_time) -{ - GList *l; - PtpDomainData *domain = NULL; - PtpPendingSync *sync = NULL; - - GST_TRACE ("Processing FOLLOW_UP message"); - - /* Don't consider messages with the alternate master flag set */ - if ((msg->flag_field & 0x0100)) { - GST_TRACE ("Ignoring FOLLOW_UP with alternate-master flag"); - return; - } - - for (l = domain_data; l; l = l->next) { - PtpDomainData *tmp = l->data; - - if (msg->domain_number == tmp->domain) { - domain = tmp; - break; - } - } - - if (!domain) { - GST_TRACE ("No domain match for FOLLOW_UP msg"); - return; - } - - /* If we have a master clock, ignore this message if it's not coming from there */ - if (domain->have_master_clock - && compare_clock_identity (&domain->master_clock_identity, - &msg->source_port_identity) != 0) { - GST_TRACE ("FOLLOW_UP msg not from current clock master. Ignoring"); - return; - } - - /* Check if we know about this one */ - for (l = domain->pending_syncs.head; l; l = l->next) { - PtpPendingSync *tmp = l->data; - - if (tmp->sync_seqnum == msg->sequence_id) { - sync = tmp; - break; - } - } - - if (!sync) { - GST_TRACE ("Ignoring FOLLOW_UP with no pending SYNC"); - return; - } - - /* Got a FOLLOW_UP for this already */ - if (sync->sync_send_time_remote != GST_CLOCK_TIME_NONE) { - GST_TRACE ("Got repeat FOLLOW_UP. Ignoring"); - return; - } - - if (sync->sync_recv_time_local >= receive_time) { - GST_ERROR ("Got bogus follow up in domain %u: %" GST_TIME_FORMAT " > %" - GST_TIME_FORMAT, domain->domain, - GST_TIME_ARGS (sync->sync_recv_time_local), - GST_TIME_ARGS (receive_time)); - g_queue_remove (&domain->pending_syncs, sync); - ptp_pending_sync_free (sync); - return; - } - - sync->correction_field_sync += msg->correction_field; - sync->sync_send_time_remote = - PTP_TIMESTAMP_TO_GST_CLOCK_TIME (msg->message_specific. - follow_up.precise_origin_timestamp); - sync->follow_up_recv_time_local = receive_time; - - if (domain->last_ptp_sync_time >= sync->sync_send_time_remote) { - GST_WARNING ("Backwards PTP times in domain %u: %" GST_TIME_FORMAT " >= %" - GST_TIME_FORMAT, domain->domain, - GST_TIME_ARGS (domain->last_ptp_sync_time), - GST_TIME_ARGS (sync->sync_send_time_remote)); - g_queue_remove (&domain->pending_syncs, sync); - ptp_pending_sync_free (sync); - sync = NULL; - return; - } - domain->last_ptp_sync_time = sync->sync_send_time_remote; - - if (send_delay_req (domain, sync)) { - /* Sent delay request */ - } else { - update_ptp_time (domain, sync); - g_queue_remove (&domain->pending_syncs, sync); - ptp_pending_sync_free (sync); - sync = NULL; - } -} - -static void -handle_delay_resp_message (PtpMessage * msg, GstClockTime receive_time) -{ - GList *l; - PtpDomainData *domain = NULL; - PtpPendingSync *sync = NULL; - - /* Don't consider messages with the alternate master flag set */ - if ((msg->flag_field & 0x0100)) - return; - - for (l = domain_data; l; l = l->next) { - PtpDomainData *tmp = l->data; - - if (msg->domain_number == tmp->domain) { - domain = tmp; - break; - } - } - - if (!domain) - return; - - /* If we have a master clock, ignore this message if it's not coming from there */ - if (domain->have_master_clock - && compare_clock_identity (&domain->master_clock_identity, - &msg->source_port_identity) != 0) - return; - - /* Not for us */ - if (msg->message_specific.delay_resp. - requesting_port_identity.clock_identity != ptp_clock_id.clock_identity - || msg->message_specific.delay_resp. - requesting_port_identity.port_number != ptp_clock_id.port_number) - return; - - domain->min_delay_req_interval = - log2_to_clock_time (msg->log_message_interval); - - /* Check if we know about this one */ - for (l = domain->pending_syncs.head; l; l = l->next) { - PtpPendingSync *tmp = l->data; - - if (tmp->delay_req_seqnum == msg->sequence_id) { - sync = tmp; - break; - } - } - - if (!sync) - return; - - /* Got a DELAY_RESP for this already */ - if (sync->delay_req_recv_time_remote != GST_CLOCK_TIME_NONE) - return; - - if (sync->delay_req_send_time_local > receive_time) { - GST_ERROR ("Got bogus delay response in domain %u: %" GST_TIME_FORMAT " > %" - GST_TIME_FORMAT, domain->domain, - GST_TIME_ARGS (sync->delay_req_send_time_local), - GST_TIME_ARGS (receive_time)); - g_queue_remove (&domain->pending_syncs, sync); - ptp_pending_sync_free (sync); - return; - } - - sync->correction_field_delay = msg->correction_field; - - sync->delay_req_recv_time_remote = - PTP_TIMESTAMP_TO_GST_CLOCK_TIME (msg->message_specific. - delay_resp.receive_timestamp); - sync->delay_resp_recv_time_local = receive_time; - - if (domain->mean_path_delay != 0 - && sync->sync_send_time_remote > sync->delay_req_recv_time_remote) { - GST_WARNING ("Sync send time after delay req receive time for domain %u: %" - GST_TIME_FORMAT " > %" GST_TIME_FORMAT, domain->domain, - GST_TIME_ARGS (sync->sync_send_time_remote), - GST_TIME_ARGS (sync->delay_req_recv_time_remote)); - g_queue_remove (&domain->pending_syncs, sync); - ptp_pending_sync_free (sync); - return; - } - - if (update_mean_path_delay (domain, sync)) - update_ptp_time (domain, sync); - g_queue_remove (&domain->pending_syncs, sync); - ptp_pending_sync_free (sync); -} - -static void -handle_ptp_message (PtpMessage * msg, GstClockTime receive_time) -{ - /* Ignore our own messages */ - if (msg->source_port_identity.clock_identity == ptp_clock_id.clock_identity && - msg->source_port_identity.port_number == ptp_clock_id.port_number) { - GST_TRACE ("Ignoring our own message"); - return; - } - - GST_TRACE ("Message type %d receive_time %" GST_TIME_FORMAT, - msg->message_type, GST_TIME_ARGS (receive_time)); - switch (msg->message_type) { - case PTP_MESSAGE_TYPE_ANNOUNCE: - handle_announce_message (msg, receive_time); - break; - case PTP_MESSAGE_TYPE_SYNC: - handle_sync_message (msg, receive_time); - break; - case PTP_MESSAGE_TYPE_FOLLOW_UP: - handle_follow_up_message (msg, receive_time); - break; - case PTP_MESSAGE_TYPE_DELAY_RESP: - handle_delay_resp_message (msg, receive_time); - break; - default: - break; - } -} - -static gboolean -have_stdin_data_cb (GIOChannel * channel, GIOCondition condition, - gpointer user_data) -{ - GIOStatus status; - StdIOHeader header; - gchar buffer[8192]; - GError *err = NULL; - gsize read; - - if ((condition & G_IO_STATUS_EOF)) { - GST_ERROR ("Got EOF on stdin"); - g_main_loop_quit (main_loop); - return G_SOURCE_REMOVE; - } - - status = - g_io_channel_read_chars (channel, (gchar *) & header, sizeof (header), - &read, &err); - if (status == G_IO_STATUS_ERROR) { - GST_ERROR ("Failed to read from stdin: %s", err->message); - g_clear_error (&err); - g_main_loop_quit (main_loop); - return G_SOURCE_REMOVE; - } else if (status == G_IO_STATUS_EOF) { - GST_ERROR ("Got EOF on stdin"); - g_main_loop_quit (main_loop); - return G_SOURCE_REMOVE; - } else if (status != G_IO_STATUS_NORMAL) { - GST_ERROR ("Unexpected stdin read status: %d", status); - g_main_loop_quit (main_loop); - return G_SOURCE_REMOVE; - } else if (read != sizeof (header)) { - GST_ERROR ("Unexpected read size: %" G_GSIZE_FORMAT, read); - g_main_loop_quit (main_loop); - return G_SOURCE_REMOVE; - } else if (header.size > 8192) { - GST_ERROR ("Unexpected size: %u", header.size); - g_main_loop_quit (main_loop); - return G_SOURCE_REMOVE; - } - - status = g_io_channel_read_chars (channel, buffer, header.size, &read, &err); - if (status == G_IO_STATUS_ERROR) { - GST_ERROR ("Failed to read from stdin: %s", err->message); - g_clear_error (&err); - g_main_loop_quit (main_loop); - return G_SOURCE_REMOVE; - } else if (status == G_IO_STATUS_EOF) { - GST_ERROR ("EOF on stdin"); - g_main_loop_quit (main_loop); - return G_SOURCE_REMOVE; - } else if (status != G_IO_STATUS_NORMAL) { - GST_ERROR ("Unexpected stdin read status: %d", status); - g_main_loop_quit (main_loop); - return G_SOURCE_REMOVE; - } else if (read != header.size) { - GST_ERROR ("Unexpected read size: %" G_GSIZE_FORMAT, read); - g_main_loop_quit (main_loop); - return G_SOURCE_REMOVE; - } - - switch (header.type) { - case TYPE_EVENT: - case TYPE_GENERAL:{ - GstClockTime receive_time = gst_clock_get_time (observation_system_clock); - PtpMessage msg; - - if (parse_ptp_message (&msg, (const guint8 *) buffer, header.size)) { - dump_ptp_message (&msg); - handle_ptp_message (&msg, receive_time); - } - break; - } - default: - case TYPE_CLOCK_ID:{ - if (header.size != 8) { - GST_ERROR ("Unexpected clock id size (%u != 8)", header.size); - g_main_loop_quit (main_loop); - return G_SOURCE_REMOVE; - } - g_mutex_lock (&ptp_lock); - ptp_clock_id.clock_identity = GST_READ_UINT64_BE (buffer); - ptp_clock_id.port_number = getpid (); - GST_DEBUG ("Got clock id 0x%016" G_GINT64_MODIFIER "x %u", - ptp_clock_id.clock_identity, ptp_clock_id.port_number); - g_cond_signal (&ptp_cond); - g_mutex_unlock (&ptp_lock); - break; - } - } - - return G_SOURCE_CONTINUE; -} - -/* Cleanup all announce messages and announce message senders - * that are timed out by now, and clean up all pending syncs - * that are missing their FOLLOW_UP or DELAY_RESP */ -static gboolean -cleanup_cb (gpointer data) -{ - GstClockTime now = gst_clock_get_time (observation_system_clock); - GList *l, *m, *n; - - for (l = domain_data; l; l = l->next) { - PtpDomainData *domain = l->data; - - for (n = domain->announce_senders; n;) { - PtpAnnounceSender *sender = n->data; - gboolean timed_out = TRUE; - - /* Keep only 5 messages per sender around */ - while (g_queue_get_length (&sender->announce_messages) > 5) { - PtpAnnounceMessage *msg = g_queue_pop_head (&sender->announce_messages); - g_free (msg); - } - - for (m = sender->announce_messages.head; m; m = m->next) { - PtpAnnounceMessage *msg = m->data; - - if (msg->receive_time + - sender->announce_interval * PTP_ANNOUNCE_RECEIPT_TIMEOUT > now) { - timed_out = FALSE; - break; - } - } - - if (timed_out) { - GST_DEBUG ("Announce sender 0x%016" G_GINT64_MODIFIER "x %u timed out", - sender->master_clock_identity.clock_identity, - sender->master_clock_identity.port_number); - g_queue_foreach (&sender->announce_messages, (GFunc) g_free, NULL); - g_queue_clear (&sender->announce_messages); - } - - if (g_queue_get_length (&sender->announce_messages) == 0) { - GList *tmp = n->next; - - if (compare_clock_identity (&sender->master_clock_identity, - &domain->master_clock_identity) == 0) - GST_WARNING ("currently selected master clock timed out"); - g_free (sender); - domain->announce_senders = - g_list_delete_link (domain->announce_senders, n); - n = tmp; - } else { - n = n->next; - } - } - select_best_master_clock (domain, now); - - /* Clean up any pending syncs */ - for (n = domain->pending_syncs.head; n;) { - PtpPendingSync *sync = n->data; - gboolean timed_out = FALSE; - - /* Time out pending syncs after 4 sync intervals or 10 seconds, - * and pending delay reqs after 4 delay req intervals or 10 seconds - */ - if (sync->delay_req_send_time_local != GST_CLOCK_TIME_NONE && - ((domain->min_delay_req_interval != 0 - && sync->delay_req_send_time_local + - 4 * domain->min_delay_req_interval < now) - || (sync->delay_req_send_time_local + 10 * GST_SECOND < now))) { - timed_out = TRUE; - } else if ((domain->sync_interval != 0 - && sync->sync_recv_time_local + 4 * domain->sync_interval < now) - || (sync->sync_recv_time_local + 10 * GST_SECOND < now)) { - timed_out = TRUE; - } - - if (timed_out) { - GList *tmp = n->next; - ptp_pending_sync_free (sync); - g_queue_delete_link (&domain->pending_syncs, n); - n = tmp; - } else { - n = n->next; - } - } - } - - return G_SOURCE_CONTINUE; -} - -static gpointer -ptp_helper_main (gpointer data) -{ - GSource *cleanup_source; - - GST_DEBUG ("Starting PTP helper loop"); - - /* Check all 5 seconds, if we have to cleanup ANNOUNCE or pending syncs message */ - cleanup_source = g_timeout_source_new_seconds (5); - g_source_set_priority (cleanup_source, G_PRIORITY_DEFAULT); - g_source_set_callback (cleanup_source, (GSourceFunc) cleanup_cb, NULL, NULL); - g_source_attach (cleanup_source, main_context); - g_source_unref (cleanup_source); - - g_main_loop_run (main_loop); - GST_DEBUG ("Stopped PTP helper loop"); - - g_mutex_lock (&ptp_lock); - ptp_clock_id.clock_identity = GST_PTP_CLOCK_ID_NONE; - ptp_clock_id.port_number = 0; - initted = FALSE; - g_cond_signal (&ptp_cond); - g_mutex_unlock (&ptp_lock); - - return NULL; -} - -/** - * gst_ptp_is_supported: - * - * Check if PTP clocks are generally supported on this system, and if previous - * initializations did not fail. - * - * Returns: %TRUE if PTP clocks are generally supported on this system, and - * previous initializations did not fail. - * - * Since: 1.6 - */ -gboolean -gst_ptp_is_supported (void) -{ - return supported; -} - -/** - * gst_ptp_is_initialized: - * - * Check if the GStreamer PTP clock subsystem is initialized. - * - * Returns: %TRUE if the GStreamer PTP clock subsystem is initialized. - * - * Since: 1.6 - */ -gboolean -gst_ptp_is_initialized (void) -{ - return initted; -} - -/** - * gst_ptp_init: - * @clock_id: PTP clock id of this process' clock or %GST_PTP_CLOCK_ID_NONE - * @interfaces: (transfer none) (array zero-terminated=1) (allow-none): network interfaces to run the clock on - * - * Initialize the GStreamer PTP subsystem and create a PTP ordinary clock in - * slave-only mode for all domains on the given @interfaces with the - * given @clock_id. - * - * If @clock_id is %GST_PTP_CLOCK_ID_NONE, a clock id is automatically - * generated from the MAC address of the first network interface. - * - * This function is automatically called by gst_ptp_clock_new() with default - * parameters if it wasn't called before. - * - * Returns: %TRUE if the GStreamer PTP clock subsystem could be initialized. - * - * Since: 1.6 - */ -gboolean -gst_ptp_init (guint64 clock_id, gchar ** interfaces) -{ - gboolean ret; - const gchar *env; - gchar **argv = NULL; - gint argc, argc_c; - gint fd_r, fd_w; - GError *err = NULL; - GSource *stdin_source; - - GST_DEBUG_CATEGORY_INIT (ptp_debug, "ptp", 0, "PTP clock"); - - g_mutex_lock (&ptp_lock); - if (!supported) { - GST_ERROR ("PTP not supported"); - ret = FALSE; - goto done; - } - - if (initted) { - GST_DEBUG ("PTP already initialized"); - ret = TRUE; - goto done; - } - - if (ptp_helper_pid) { - GST_DEBUG ("PTP currently initializing"); - goto wait; - } - - if (!domain_stats_hooks_initted) { - g_hook_list_init (&domain_stats_hooks, sizeof (GHook)); - domain_stats_hooks_initted = TRUE; - } - - argc = 1; - if (clock_id != GST_PTP_CLOCK_ID_NONE) - argc += 2; - if (interfaces != NULL) - argc += 2 * g_strv_length (interfaces); - - argv = g_new0 (gchar *, argc + 2); - argc_c = 0; - - env = g_getenv ("GST_PTP_HELPER_1_0"); - if (env == NULL) - env = g_getenv ("GST_PTP_HELPER"); - if (env != NULL && *env != '\0') { - GST_LOG ("Trying GST_PTP_HELPER env var: %s", env); - argv[argc_c++] = g_strdup (env); - } else { - argv[argc_c++] = g_strdup (GST_PTP_HELPER_INSTALLED); - } - - if (clock_id != GST_PTP_CLOCK_ID_NONE) { - argv[argc_c++] = g_strdup ("-c"); - argv[argc_c++] = g_strdup_printf ("0x%016" G_GINT64_MODIFIER "x", clock_id); - } - - if (interfaces != NULL) { - gchar **ptr = interfaces; - - while (*ptr) { - argv[argc_c++] = g_strdup ("-i"); - argv[argc_c++] = g_strdup (*ptr); - ptr++; - } - } - - main_context = g_main_context_new (); - main_loop = g_main_loop_new (main_context, FALSE); - - ptp_helper_thread = - g_thread_try_new ("ptp-helper-thread", ptp_helper_main, NULL, &err); - if (!ptp_helper_thread) { - GST_ERROR ("Failed to start PTP helper thread: %s", err->message); - g_clear_error (&err); - ret = FALSE; - goto done; - } - - if (!g_spawn_async_with_pipes (NULL, argv, NULL, 0, NULL, NULL, - &ptp_helper_pid, &fd_w, &fd_r, NULL, &err)) { - GST_ERROR ("Failed to start ptp helper process: %s", err->message); - g_clear_error (&err); - ret = FALSE; - supported = FALSE; - goto done; - } - - stdin_channel = g_io_channel_unix_new (fd_r); - g_io_channel_set_encoding (stdin_channel, NULL, NULL); - g_io_channel_set_buffered (stdin_channel, FALSE); - g_io_channel_set_close_on_unref (stdin_channel, TRUE); - stdin_source = - g_io_create_watch (stdin_channel, G_IO_IN | G_IO_PRI | G_IO_HUP); - g_source_set_priority (stdin_source, G_PRIORITY_DEFAULT); - g_source_set_callback (stdin_source, (GSourceFunc) have_stdin_data_cb, NULL, - NULL); - g_source_attach (stdin_source, main_context); - g_source_unref (stdin_source); - - /* Create stdout channel */ - stdout_channel = g_io_channel_unix_new (fd_w); - g_io_channel_set_encoding (stdout_channel, NULL, NULL); - g_io_channel_set_close_on_unref (stdout_channel, TRUE); - g_io_channel_set_buffered (stdout_channel, FALSE); - - delay_req_rand = g_rand_new (); - observation_system_clock = - g_object_new (GST_TYPE_SYSTEM_CLOCK, "name", "ptp-observation-clock", - NULL); - gst_object_ref_sink (observation_system_clock); - - initted = TRUE; - -wait: - GST_DEBUG ("Waiting for PTP to be initialized"); - - while (ptp_clock_id.clock_identity == GST_PTP_CLOCK_ID_NONE && initted) - g_cond_wait (&ptp_cond, &ptp_lock); - - ret = initted; - if (ret) { - GST_DEBUG ("Initialized and got clock id 0x%016" G_GINT64_MODIFIER "x %u", - ptp_clock_id.clock_identity, ptp_clock_id.port_number); - } else { - GST_ERROR ("Failed to initialize"); - supported = FALSE; - } - -done: - g_strfreev (argv); - - if (!ret) { - if (ptp_helper_pid) { -#ifndef G_OS_WIN32 - kill (ptp_helper_pid, SIGKILL); - waitpid (ptp_helper_pid, NULL, 0); -#else - TerminateProcess (ptp_helper_pid, 1); - WaitForSingleObject (ptp_helper_pid, INFINITE); -#endif - g_spawn_close_pid (ptp_helper_pid); - } - ptp_helper_pid = 0; - - if (stdin_channel) - g_io_channel_unref (stdin_channel); - stdin_channel = NULL; - if (stdout_channel) - g_io_channel_unref (stdout_channel); - stdout_channel = NULL; - - if (main_loop && ptp_helper_thread) { - g_main_loop_quit (main_loop); - g_thread_join (ptp_helper_thread); - } - ptp_helper_thread = NULL; - if (main_loop) - g_main_loop_unref (main_loop); - main_loop = NULL; - if (main_context) - g_main_context_unref (main_context); - main_context = NULL; - - if (delay_req_rand) - g_rand_free (delay_req_rand); - delay_req_rand = NULL; - - if (observation_system_clock) - gst_object_unref (observation_system_clock); - observation_system_clock = NULL; - } - - g_mutex_unlock (&ptp_lock); - - return ret; -} - -/** - * gst_ptp_deinit: - * - * Deinitialize the GStreamer PTP subsystem and stop the PTP clock. If there - * are any remaining GstPtpClock instances, they won't be further synchronized - * to the PTP network clock. - * - * Since: 1.6 - */ -void -gst_ptp_deinit (void) -{ - GList *l, *m; - - g_mutex_lock (&ptp_lock); - - if (ptp_helper_pid) { -#ifndef G_OS_WIN32 - kill (ptp_helper_pid, SIGKILL); - waitpid (ptp_helper_pid, NULL, 0); -#else - TerminateProcess (ptp_helper_pid, 1); - WaitForSingleObject (ptp_helper_pid, INFINITE); -#endif - g_spawn_close_pid (ptp_helper_pid); - } - ptp_helper_pid = 0; - - if (stdin_channel) - g_io_channel_unref (stdin_channel); - stdin_channel = NULL; - if (stdout_channel) - g_io_channel_unref (stdout_channel); - stdout_channel = NULL; - - if (main_loop && ptp_helper_thread) { - GThread *tmp = ptp_helper_thread; - ptp_helper_thread = NULL; - g_mutex_unlock (&ptp_lock); - g_main_loop_quit (main_loop); - g_thread_join (tmp); - g_mutex_lock (&ptp_lock); - } - if (main_loop) - g_main_loop_unref (main_loop); - main_loop = NULL; - if (main_context) - g_main_context_unref (main_context); - main_context = NULL; - - if (delay_req_rand) - g_rand_free (delay_req_rand); - delay_req_rand = NULL; - if (observation_system_clock) - gst_object_unref (observation_system_clock); - observation_system_clock = NULL; - - for (l = domain_data; l; l = l->next) { - PtpDomainData *domain = l->data; - - for (m = domain->announce_senders; m; m = m->next) { - PtpAnnounceSender *sender = m->data; - - g_queue_foreach (&sender->announce_messages, (GFunc) g_free, NULL); - g_queue_clear (&sender->announce_messages); - g_free (sender); - } - g_list_free (domain->announce_senders); - - g_queue_foreach (&domain->pending_syncs, (GFunc) ptp_pending_sync_free, - NULL); - g_queue_clear (&domain->pending_syncs); - gst_object_unref (domain->domain_clock); - g_free (domain); - } - g_list_free (domain_data); - domain_data = NULL; - g_list_foreach (domain_clocks, (GFunc) g_free, NULL); - g_list_free (domain_clocks); - domain_clocks = NULL; - - ptp_clock_id.clock_identity = GST_PTP_CLOCK_ID_NONE; - ptp_clock_id.port_number = 0; - - initted = FALSE; - - g_mutex_unlock (&ptp_lock); -} - -#define DEFAULT_DOMAIN 0 - -enum -{ - PROP_0, - PROP_DOMAIN, - PROP_INTERNAL_CLOCK, - PROP_MASTER_CLOCK_ID, - PROP_GRANDMASTER_CLOCK_ID -}; - -struct _GstPtpClockPrivate -{ - guint domain; - GstClock *domain_clock; - gulong domain_stats_id; -}; - -#define gst_ptp_clock_parent_class parent_class -G_DEFINE_TYPE_WITH_PRIVATE (GstPtpClock, gst_ptp_clock, GST_TYPE_SYSTEM_CLOCK); - -static void gst_ptp_clock_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_ptp_clock_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); -static void gst_ptp_clock_finalize (GObject * object); - -static GstClockTime gst_ptp_clock_get_internal_time (GstClock * clock); - -static void -gst_ptp_clock_class_init (GstPtpClockClass * klass) -{ - GObjectClass *gobject_class; - GstClockClass *clock_class; - - gobject_class = G_OBJECT_CLASS (klass); - clock_class = GST_CLOCK_CLASS (klass); - - gobject_class->finalize = gst_ptp_clock_finalize; - gobject_class->get_property = gst_ptp_clock_get_property; - gobject_class->set_property = gst_ptp_clock_set_property; - - g_object_class_install_property (gobject_class, PROP_DOMAIN, - g_param_spec_uint ("domain", "Domain", - "The PTP domain", 0, G_MAXUINT8, - DEFAULT_DOMAIN, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_INTERNAL_CLOCK, - g_param_spec_object ("internal-clock", "Internal Clock", - "Internal clock", GST_TYPE_CLOCK, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_MASTER_CLOCK_ID, - g_param_spec_uint64 ("master-clock-id", "Master Clock ID", - "Master Clock ID", 0, G_MAXUINT64, 0, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_GRANDMASTER_CLOCK_ID, - g_param_spec_uint64 ("grandmaster-clock-id", "Grand Master Clock ID", - "Grand Master Clock ID", 0, G_MAXUINT64, 0, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - clock_class->get_internal_time = gst_ptp_clock_get_internal_time; -} - -static void -gst_ptp_clock_init (GstPtpClock * self) -{ - GstPtpClockPrivate *priv; - - self->priv = priv = gst_ptp_clock_get_instance_private (self); - - GST_OBJECT_FLAG_SET (self, GST_CLOCK_FLAG_CAN_SET_MASTER); - GST_OBJECT_FLAG_SET (self, GST_CLOCK_FLAG_NEEDS_STARTUP_SYNC); - - priv->domain = DEFAULT_DOMAIN; -} - -static gboolean -gst_ptp_clock_ensure_domain_clock (GstPtpClock * self) -{ - gboolean got_clock = TRUE; - - if (G_UNLIKELY (!self->priv->domain_clock)) { - g_mutex_lock (&domain_clocks_lock); - if (!self->priv->domain_clock) { - GList *l; - - got_clock = FALSE; - - for (l = domain_clocks; l; l = l->next) { - PtpDomainData *clock_data = l->data; - - if (clock_data->domain == self->priv->domain && - clock_data->have_master_clock && clock_data->last_ptp_time != 0) { - GST_DEBUG ("Switching domain clock on domain %d", clock_data->domain); - self->priv->domain_clock = clock_data->domain_clock; - got_clock = TRUE; - break; - } - } - } - g_mutex_unlock (&domain_clocks_lock); - if (got_clock) { - g_object_notify (G_OBJECT (self), "internal-clock"); - gst_clock_set_synced (GST_CLOCK (self), TRUE); - } - } - - return got_clock; -} - -static gboolean -gst_ptp_clock_stats_callback (guint8 domain, const GstStructure * stats, - gpointer user_data) -{ - GstPtpClock *self = user_data; - - if (domain != self->priv->domain - || !gst_structure_has_name (stats, GST_PTP_STATISTICS_TIME_UPDATED)) - return TRUE; - - /* Let's set our internal clock */ - if (!gst_ptp_clock_ensure_domain_clock (self)) - return TRUE; - - self->priv->domain_stats_id = 0; - - return FALSE; -} - -static void -gst_ptp_clock_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstPtpClock *self = GST_PTP_CLOCK (object); - - switch (prop_id) { - case PROP_DOMAIN: - self->priv->domain = g_value_get_uint (value); - gst_ptp_clock_ensure_domain_clock (self); - if (!self->priv->domain_clock) - self->priv->domain_stats_id = - gst_ptp_statistics_callback_add (gst_ptp_clock_stats_callback, self, - NULL); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_ptp_clock_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstPtpClock *self = GST_PTP_CLOCK (object); - - switch (prop_id) { - case PROP_DOMAIN: - g_value_set_uint (value, self->priv->domain); - break; - case PROP_INTERNAL_CLOCK: - gst_ptp_clock_ensure_domain_clock (self); - g_value_set_object (value, self->priv->domain_clock); - break; - case PROP_MASTER_CLOCK_ID: - case PROP_GRANDMASTER_CLOCK_ID:{ - GList *l; - - g_mutex_lock (&domain_clocks_lock); - g_value_set_uint64 (value, 0); - - for (l = domain_clocks; l; l = l->next) { - PtpDomainData *clock_data = l->data; - - if (clock_data->domain == self->priv->domain) { - if (prop_id == PROP_MASTER_CLOCK_ID) - g_value_set_uint64 (value, - clock_data->master_clock_identity.clock_identity); - else - g_value_set_uint64 (value, clock_data->grandmaster_identity); - break; - } - } - g_mutex_unlock (&domain_clocks_lock); - break; - } - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_ptp_clock_finalize (GObject * object) -{ - GstPtpClock *self = GST_PTP_CLOCK (object); - - if (self->priv->domain_stats_id) - gst_ptp_statistics_callback_remove (self->priv->domain_stats_id); - - G_OBJECT_CLASS (gst_ptp_clock_parent_class)->finalize (object); -} - -static GstClockTime -gst_ptp_clock_get_internal_time (GstClock * clock) -{ - GstPtpClock *self = GST_PTP_CLOCK (clock); - - gst_ptp_clock_ensure_domain_clock (self); - - if (!self->priv->domain_clock) { - GST_ERROR_OBJECT (self, "Domain %u has no clock yet and is not synced", - self->priv->domain); - return GST_CLOCK_TIME_NONE; - } - - return gst_clock_get_time (self->priv->domain_clock); -} - -/** - * gst_ptp_clock_new: - * @name: Name of the clock - * @domain: PTP domain - * - * Creates a new PTP clock instance that exports the PTP time of the master - * clock in @domain. This clock can be slaved to other clocks as needed. - * - * If gst_ptp_init() was not called before, this will call gst_ptp_init() with - * default parameters. - * - * This clock only returns valid timestamps after it received the first - * times from the PTP master clock on the network. Once this happens the - * GstPtpClock::internal-clock property will become non-NULL. You can - * check this with gst_clock_wait_for_sync(), the GstClock::synced signal and - * gst_clock_is_synced(). - * - * Returns: (transfer full): A new #GstClock - * - * Since: 1.6 - */ -GstClock * -gst_ptp_clock_new (const gchar * name, guint domain) -{ - GstClock *clock; - - g_return_val_if_fail (domain <= G_MAXUINT8, NULL); - - if (!initted && !gst_ptp_init (GST_PTP_CLOCK_ID_NONE, NULL)) { - GST_ERROR ("Failed to initialize PTP"); - return NULL; - } - - clock = g_object_new (GST_TYPE_PTP_CLOCK, "name", name, "domain", domain, - NULL); - - /* Clear floating flag */ - gst_object_ref_sink (clock); - - return clock; -} - -typedef struct -{ - guint8 domain; - const GstStructure *stats; -} DomainStatsMarshalData; - -static void -domain_stats_marshaller (GHook * hook, DomainStatsMarshalData * data) -{ - GstPtpStatisticsCallback callback = (GstPtpStatisticsCallback) hook->func; - - if (!callback (data->domain, data->stats, hook->data)) - g_hook_destroy (&domain_stats_hooks, hook->hook_id); -} - -static void -emit_ptp_statistics (guint8 domain, const GstStructure * stats) -{ - DomainStatsMarshalData data = { domain, stats }; - - g_mutex_lock (&ptp_lock); - g_hook_list_marshal (&domain_stats_hooks, TRUE, - (GHookMarshaller) domain_stats_marshaller, &data); - g_mutex_unlock (&ptp_lock); -} - -/** - * gst_ptp_statistics_callback_add: - * @callback: GstPtpStatisticsCallback to call - * @user_data: Data to pass to the callback - * @destroy_data: GDestroyNotify to destroy the data - * - * Installs a new statistics callback for gathering PTP statistics. See - * GstPtpStatisticsCallback for a list of statistics that are provided. - * - * Returns: Id for the callback that can be passed to - * gst_ptp_statistics_callback_remove() - * - * Since: 1.6 - */ -gulong -gst_ptp_statistics_callback_add (GstPtpStatisticsCallback callback, - gpointer user_data, GDestroyNotify destroy_data) -{ - GHook *hook; - - g_mutex_lock (&ptp_lock); - - if (!domain_stats_hooks_initted) { - g_hook_list_init (&domain_stats_hooks, sizeof (GHook)); - domain_stats_hooks_initted = TRUE; - } - - hook = g_hook_alloc (&domain_stats_hooks); - hook->func = callback; - hook->data = user_data; - hook->destroy = destroy_data; - g_hook_prepend (&domain_stats_hooks, hook); - g_atomic_int_add (&domain_stats_n_hooks, 1); - - g_mutex_unlock (&ptp_lock); - - return hook->hook_id; -} - -/** - * gst_ptp_statistics_callback_remove: - * @id: Callback id to remove - * - * Removes a PTP statistics callback that was previously added with - * gst_ptp_statistics_callback_add(). - * - * Since: 1.6 - */ -void -gst_ptp_statistics_callback_remove (gulong id) -{ - g_mutex_lock (&ptp_lock); - if (g_hook_destroy (&domain_stats_hooks, id)) - g_atomic_int_add (&domain_stats_n_hooks, -1); - g_mutex_unlock (&ptp_lock); -} diff --git a/libs/gst/net/gstptpclock.h b/libs/gst/net/gstptpclock.h deleted file mode 100644 index 3182431b9f..0000000000 --- a/libs/gst/net/gstptpclock.h +++ /dev/null @@ -1,161 +0,0 @@ -/* GStreamer - * Copyright (C) 2015 Sebastian Dröge <sebastian@centricular.com> - * - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_PTP_CLOCK_H__ -#define __GST_PTP_CLOCK_H__ - -#include <gst/gst.h> -#include <gst/gstsystemclock.h> -#include <gst/net/net-prelude.h> - -G_BEGIN_DECLS - -#define GST_TYPE_PTP_CLOCK \ - (gst_ptp_clock_get_type()) -#define GST_PTP_CLOCK(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PTP_CLOCK,GstPtpClock)) -#define GST_PTP_CLOCK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PTP_CLOCK,GstPtpClockClass)) -#define GST_IS_PTP_CLOCK(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PTP_CLOCK)) -#define GST_IS_PTP_CLOCK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PTP_CLOCK)) - -typedef struct _GstPtpClock GstPtpClock; -typedef struct _GstPtpClockClass GstPtpClockClass; -typedef struct _GstPtpClockPrivate GstPtpClockPrivate; - -/** - * GstPtpClock: - * - * Opaque #GstPtpClock structure. - */ -struct _GstPtpClock { - GstSystemClock clock; - - /*< private >*/ - GstPtpClockPrivate *priv; - - gpointer _gst_reserved[GST_PADDING]; -}; - -/** - * GstPtpClockClass: - * @parent_class: parented to #GstSystemClockClass - * - * Opaque #GstPtpClockClass structure. - */ -struct _GstPtpClockClass { - GstSystemClockClass parent_class; - - /*< private >*/ - gpointer _gst_reserved[GST_PADDING]; -}; - -/** - * GST_PTP_CLOCK_ID_NONE: - * PTP clock identification that can be passed to gst_ptp_init() to - * automatically select one based on the MAC address of interfaces - */ -#define GST_PTP_CLOCK_ID_NONE ((guint64) -1) - -GST_NET_API -GType gst_ptp_clock_get_type (void); - -GST_NET_API -gboolean gst_ptp_is_supported (void); - -GST_NET_API -gboolean gst_ptp_is_initialized (void); - -GST_NET_API -gboolean gst_ptp_init (guint64 clock_id, - gchar ** interfaces); -GST_NET_API -void gst_ptp_deinit (void); - -#define GST_PTP_STATISTICS_NEW_DOMAIN_FOUND "GstPtpStatisticsNewDomainFound" -#define GST_PTP_STATISTICS_BEST_MASTER_CLOCK_SELECTED "GstPtpStatisticsBestMasterClockSelected" -#define GST_PTP_STATISTICS_PATH_DELAY_MEASURED "GstPtpStatisticsPathDelayMeasured" -#define GST_PTP_STATISTICS_TIME_UPDATED "GstPtpStatisticsTimeUpdated" - -/** - * GstPtpStatisticsCallback: - * @domain: PTP domain identifier - * @stats: New statistics - * @user_data: Data passed to gst_ptp_statistics_callback_add() - * - * The statistics can be the following structures: - * - * GST_PTP_STATISTICS_NEW_DOMAIN_FOUND: - * "domain" G_TYPE_UINT The domain identifier of the domain - * "clock" GST_TYPE_CLOCK The internal clock that is slaved to the - * PTP domain - * - * GST_PTP_STATISTICS_BEST_MASTER_CLOCK_SELECTED: - * "domain" G_TYPE_UINT The domain identifier of the domain - * "master-clock-id" G_TYPE_UINT64 PTP clock identifier of the selected master - * clock - * "master-clock-port" G_TYPE_UINT PTP port number of the selected master clock - * "grandmaster-clock-id" G_TYPE_UINT64 PTP clock identifier of the grandmaster clock - * - * GST_PTP_STATISTICS_PATH_DELAY_MEASURED: - * "domain" G_TYPE_UINT The domain identifier of the domain - * "mean-path-delay-avg" GST_TYPE_CLOCK_TIME Average mean path delay - * "mean-path-delay" GST_TYPE_CLOCK_TIME Latest mean path delay - * "delay-request-delay" GST_TYPE_CLOCK_TIME Delay of DELAY_REQ / DELAY_RESP messages - * - * GST_PTP_STATISTICS_TIME_UPDATED: - * "domain" G_TYPE_UINT The domain identifier of the domain - * "mean-path-delay-avg" GST_TYPE_CLOCK_TIME Average mean path delay - * "local-time" GST_TYPE_CLOCK_TIME Local time that corresponds to ptp-time - * "ptp-time" GST_TYPE_CLOCK_TIME Newly measured PTP time at local-time - * "estimated-ptp-time" GST_TYPE_CLOCK_TIME Estimated PTP time based on previous measurements - * "discontinuity" G_TYPE_INT64 Difference between estimated and measured PTP time - * "synced" G_TYPE_BOOLEAN Currently synced to the remote clock - * "r-squared" G_TYPE_DOUBLE R² of clock estimation regression - * "internal-time" GST_TYPE_CLOCK_TIME Internal time clock parameter - * "external-time" GST_TYPE_CLOCK_TIME External time clock parameter - * "rate-num" G_TYPE_UINT64 Internal/external rate numerator - * "rate-den" G_TYPE_UINT64 Internal/external rate denominator - * "rate" G_TYPE_DOUBLE Internal/external rate - * - * If %FALSE is returned, the callback is removed and never called again. - * - */ -typedef gboolean (*GstPtpStatisticsCallback) (guint8 domain, - const GstStructure * stats, - gpointer user_data); -GST_NET_API -gulong gst_ptp_statistics_callback_add (GstPtpStatisticsCallback callback, - gpointer user_data, GDestroyNotify destroy_data); -GST_NET_API -void gst_ptp_statistics_callback_remove (gulong id); - -GST_NET_API -GstClock* gst_ptp_clock_new (const gchar *name, - guint domain); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstPtpClock, gst_object_unref) - -G_END_DECLS - -#endif /* __GST_PTP_CLOCK_H__ */ - diff --git a/libs/gst/net/meson.build b/libs/gst/net/meson.build deleted file mode 100644 index f63ae281b7..0000000000 --- a/libs/gst/net/meson.build +++ /dev/null @@ -1,69 +0,0 @@ -gst_net_sources = [ - 'gstnetaddressmeta.c', - 'gstnetclientclock.c', - 'gstnetcontrolmessagemeta.c', - 'gstnettimepacket.c', - 'gstnettimeprovider.c', - 'gstptpclock.c', - 'gstntppacket.c', - 'gstnetutils.c', -] - -gst_net_headers = [ - 'gstnet.h', - 'gstnetaddressmeta.h', - 'gstnetclientclock.h', - 'gstnetcontrolmessagemeta.h', - 'gstnettimepacket.h', - 'gstnettimeprovider.h', - 'gstnetutils.h', - 'gstptpclock.h', - 'net-prelude.h', - 'net.h', -] -install_headers(gst_net_headers, subdir : 'gstreamer-1.0/gst/net/') - -gst_net_gen_sources = [] -gst_net = library('gstnet-@0@'.format(apiversion), - gst_net_sources, - c_args : gst_c_args + ['-DBUILDING_GST_NET'], - include_directories : [configinc, libsinc], - version : libversion, - soversion : soversion, - darwin_versions : osxversion, - install : true, - dependencies : [gio_dep, gst_base_dep], -) - -pkgconfig.generate(gst_net, - libraries : [libgst], - variables : pkgconfig_variables, - subdirs : pkgconfig_subdirs, - name : 'gstreamer-net-1.0', - description : 'Network-enabled GStreamer plug-ins and clocking', -) - -if build_gir - gst_gir_extra_args = gir_init_section + [ '--c-include=gst/net/net.h' ] - gst_net_gir = gnome.generate_gir(gst_net, - sources : gst_net_sources + gst_net_headers, - namespace : 'GstNet', - nsversion : apiversion, - identifier_prefix : 'Gst', - symbol_prefix : 'gst', - export_packages : 'gstreamer-net-1.0', - dependencies : [gst_base_dep], - include_directories : [configinc, libsinc], - includes : ['GLib-2.0', 'GObject-2.0', 'GModule-2.0', 'Gio-2.0', 'Gst-1.0'], - install : true, - extra_args : gst_gir_extra_args, - ) - gst_net_gen_sources += [gst_net_gir] -endif - -gst_net_dep = declare_dependency(link_with : gst_net, - include_directories : [libsinc], - sources: gst_net_gen_sources, - dependencies : [gst_base_dep]) - -meson.override_dependency('gstreamer-net-1.0', gst_net_dep) diff --git a/libs/gst/net/net-prelude.h b/libs/gst/net/net-prelude.h deleted file mode 100644 index 47224fe048..0000000000 --- a/libs/gst/net/net-prelude.h +++ /dev/null @@ -1,35 +0,0 @@ -/* GStreamer Net Library - * Copyright (C) 2018 GStreamer developers - * - * net-prelude.h: prelude include header for gst-net library - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_NET_PRELUDE_H__ -#define __GST_NET_PRELUDE_H__ - -#include <gst/gst.h> - -#ifndef GST_NET_API -#ifdef BUILDING_GST_NET -#define GST_NET_API GST_API_EXPORT /* from config.h */ -#else -#define GST_NET_API GST_API_IMPORT -#endif -#endif - -#endif /* __GST_NET_PRELUDE_H__ */ diff --git a/libs/gst/net/net.h b/libs/gst/net/net.h deleted file mode 100644 index 4a11a94f23..0000000000 --- a/libs/gst/net/net.h +++ /dev/null @@ -1,36 +0,0 @@ -/* GStreamer - * Copyright (C) 2012 GStreamer developers - * - * net.h: single include header for gst-net library - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __GST_NET__H__ -#define __GST_NET__H__ - -#include <gst/net/net-prelude.h> - -#include <gst/net/gstnet.h> -#include <gst/net/gstnetaddressmeta.h> -#include <gst/net/gstnetcontrolmessagemeta.h> -#include <gst/net/gstnetclientclock.h> -#include <gst/net/gstnettimepacket.h> -#include <gst/net/gstnettimeprovider.h> -#include <gst/net/gstnetutils.h> -#include <gst/net/gstptpclock.h> - -#endif /* __GST_NET__H__ */ diff --git a/libs/meson.build b/libs/meson.build deleted file mode 100644 index 668dcbaaff..0000000000 --- a/libs/meson.build +++ /dev/null @@ -1 +0,0 @@ -subdir('gst') |