summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl-Anton Ingmarsson <ca.ingmarsson@gmail.com>2010-07-30 10:55:56 +0200
committerCarl-Anton Ingmarsson <ca.ingmarsson@gmail.com>2010-07-30 11:33:13 +0200
commit7320eedc04602afc46994256093cf49a8e444d17 (patch)
treeb5f1f89c5b4053837a9a70648075f6ddf814320f
parent093fcacc20a9a84a12e59f027a0f0921ff02d304 (diff)
vdpau: add GstVdpBufferPool base class
GstVdpBufferPool will be used to cache GstVdp[Video|Output]Buffers since creating these can be a costly operation on some hardware.
-rw-r--r--sys/vdpau/gstvdp/Makefile.am4
-rw-r--r--sys/vdpau/gstvdp/gstvdpbuffer.c86
-rw-r--r--sys/vdpau/gstvdp/gstvdpbuffer.h59
-rw-r--r--sys/vdpau/gstvdp/gstvdpbufferpool.c358
-rw-r--r--sys/vdpau/gstvdp/gstvdpbufferpool.h74
5 files changed, 581 insertions, 0 deletions
diff --git a/sys/vdpau/gstvdp/Makefile.am b/sys/vdpau/gstvdp/Makefile.am
index 13ba6bdef..44a0b9e95 100644
--- a/sys/vdpau/gstvdp/Makefile.am
+++ b/sys/vdpau/gstvdp/Makefile.am
@@ -5,2 +5,4 @@ libgstvdp_@GST_MAJORMINOR@_la_SOURCES = \
gstvdputils.c \
+ gstvdpbuffer.c \
+ gstvdpbufferpool.c \
gstvdpvideobuffer.c \
@@ -16,2 +18,4 @@ libgstvdp_@GST_MAJORMINOR@include_HEADERS = \
gstvdputils.h \
+ gstvdpbuffer.h \
+ gstvdpbufferpool.h \
gstvdpvideobuffer.h \
diff --git a/sys/vdpau/gstvdp/gstvdpbuffer.c b/sys/vdpau/gstvdp/gstvdpbuffer.c
new file mode 100644
index 000000000..c2292cd7d
--- /dev/null
+++ b/sys/vdpau/gstvdp/gstvdpbuffer.c
@@ -0,0 +1,86 @@
+/*
+ * GStreamer
+ * Copyright (C) 2009 Carl-Anton Ingmarsson <ca.ingmarsson@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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstvdpbuffer.h"
+
+static GObjectClass *gst_vdp_buffer_parent_class;
+
+void
+gst_vdp_buffer_set_buffer_pool (GstVdpBuffer * buffer, GstVdpBufferPool * bpool)
+{
+ g_return_if_fail (GST_IS_VDP_BUFFER (buffer));
+
+ if (bpool) {
+ g_return_if_fail (GST_IS_VDP_BUFFER_POOL (bpool));
+ g_object_add_weak_pointer (G_OBJECT (bpool), (void **) &buffer->bpool);
+ }
+
+ buffer->bpool = bpool;
+}
+
+gboolean
+gst_vdp_buffer_revive (GstVdpBuffer * buffer)
+{
+ if (buffer->bpool)
+ return gst_vdp_buffer_pool_put_buffer (buffer->bpool, buffer);
+
+ return FALSE;
+}
+
+static void
+gst_vdp_buffer_init (GstVdpBuffer * buffer, gpointer g_class)
+{
+ buffer->bpool = NULL;
+}
+
+static void
+gst_vdp_buffer_class_init (gpointer g_class, gpointer class_data)
+{
+ gst_vdp_buffer_parent_class = g_type_class_peek_parent (g_class);
+}
+
+
+GType
+gst_vdp_buffer_get_type (void)
+{
+ static GType _gst_vdp_buffer_type;
+
+ if (G_UNLIKELY (_gst_vdp_buffer_type == 0)) {
+ static const GTypeInfo info = {
+ sizeof (GstBufferClass),
+ NULL,
+ NULL,
+ gst_vdp_buffer_class_init,
+ NULL,
+ NULL,
+ sizeof (GstVdpBuffer),
+ 0,
+ (GInstanceInitFunc) gst_vdp_buffer_init,
+ NULL
+ };
+ _gst_vdp_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
+ "GstVdpBuffer", &info, 0);
+ }
+ return _gst_vdp_buffer_type;
+}
diff --git a/sys/vdpau/gstvdp/gstvdpbuffer.h b/sys/vdpau/gstvdp/gstvdpbuffer.h
new file mode 100644
index 000000000..fc8b57812
--- /dev/null
+++ b/sys/vdpau/gstvdp/gstvdpbuffer.h
@@ -0,0 +1,59 @@
+/*
+ * GStreamer
+ * Copyright (C) 2009 Carl-Anton Ingmarsson <ca.ingmarsson@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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _GST_VDP_BUFFER_H_
+#define _GST_VDP_BUFFER_H_
+
+#include <gst/gst.h>
+
+typedef struct _GstVdpBuffer GstVdpBuffer;
+
+#include "gstvdpbufferpool.h"
+
+#define GST_TYPE_VDP_BUFFER (gst_vdp_buffer_get_type())
+
+#define GST_IS_VDP_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VDP_BUFFER))
+#define GST_VDP_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VDP_BUFFER, GstVdpBuffer))
+#define GST_VDP_BUFFER_CAST(obj) ((GstVdpBuffer *)obj)
+
+struct _GstVdpBuffer {
+ GstBuffer buffer;
+
+ GstVdpBufferPool *bpool;
+};
+
+void gst_vdp_buffer_set_buffer_pool (GstVdpBuffer *buffer, GstVdpBufferPool *bpool);
+gboolean gst_vdp_buffer_revive (GstVdpBuffer * buffer);
+
+static inline GstVdpBuffer *
+gst_vdp_buffer_ref (GstVdpBuffer *buffer)
+{
+ return (GstVdpBuffer *) gst_mini_object_ref (GST_MINI_OBJECT_CAST (buffer));
+}
+
+static inline void
+gst_vdp_buffer_unref (GstVdpBuffer *buffer)
+{
+ gst_mini_object_unref (GST_MINI_OBJECT_CAST (buffer));
+}
+
+GType gst_vdp_buffer_get_type (void);
+
+#endif \ No newline at end of file
diff --git a/sys/vdpau/gstvdp/gstvdpbufferpool.c b/sys/vdpau/gstvdp/gstvdpbufferpool.c
new file mode 100644
index 000000000..fea8aea4f
--- /dev/null
+++ b/sys/vdpau/gstvdp/gstvdpbufferpool.c
@@ -0,0 +1,358 @@
+/*
+ * GStreamer
+ * Copyright (C) 2010 Carl-Anton Ingmarsson <ca.ingmarsson@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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include "gstvdpbufferpool.h"
+
+struct _GstVdpBufferPoolPrivate
+{
+ GQueue *buffers;
+ GMutex *mutex;
+
+ /* properties */
+ guint max_buffers;
+ GstCaps *caps;
+ GstVdpDevice *device;
+};
+
+enum
+{
+ PROP_0,
+ PROP_DEVICE,
+ PROP_CAPS,
+ PROP_MAX_BUFFERS
+};
+
+G_DEFINE_TYPE (GstVdpBufferPool, gst_vdp_buffer_pool, G_TYPE_OBJECT);
+
+#define DEFAULT_MAX_BUFFERS 20
+
+static void
+gst_vdp_buffer_free (GstVdpBuffer * buf)
+{
+ gst_vdp_buffer_set_buffer_pool (buf, NULL);
+ gst_vdp_buffer_unref (buf);
+}
+
+static void
+gst_vdp_buffer_pool_clear (GstVdpBufferPool * bpool)
+{
+ GstVdpBufferPoolPrivate *priv = bpool->priv;
+
+ g_queue_foreach (priv->buffers, (GFunc) gst_vdp_buffer_free, NULL);
+ g_queue_clear (priv->buffers);
+}
+
+gboolean
+gst_vdp_buffer_pool_put_buffer (GstVdpBufferPool * bpool, GstVdpBuffer * buf)
+{
+ GstVdpBufferPoolPrivate *priv;
+
+ gboolean res;
+ GstVdpBufferPoolClass *bpool_class;
+ GstCaps *caps;
+
+ g_return_val_if_fail (GST_IS_VDP_BUFFER_POOL (bpool), FALSE);
+ g_return_val_if_fail (GST_IS_VDP_BUFFER (buf), FALSE);
+
+ priv = bpool->priv;
+ g_return_val_if_fail (priv->caps, FALSE);
+
+ g_mutex_lock (priv->mutex);
+
+ if (priv->buffers->length == priv->max_buffers) {
+ res = FALSE;
+ goto done;
+ }
+
+ bpool_class = GST_VDP_BUFFER_POOL_GET_CLASS (bpool);
+ caps = GST_BUFFER_CAPS (buf);
+ if (!caps)
+ goto no_caps;
+
+ if (!bpool_class->check_caps (bpool, caps)) {
+ res = FALSE;
+ goto done;
+ }
+
+ gst_vdp_buffer_ref (buf);
+ g_queue_push_tail (priv->buffers, buf);
+ res = TRUE;
+
+done:
+ g_mutex_unlock (priv->mutex);
+
+ return res;
+
+no_caps:
+ GST_WARNING ("Buffer doesn't have any caps");
+ res = FALSE;
+ goto done;
+}
+
+GstVdpBuffer *
+gst_vdp_buffer_pool_get_buffer (GstVdpBufferPool * bpool, GError ** error)
+{
+ GstVdpBufferPoolPrivate *priv;
+ GstVdpBuffer *buf;
+
+ g_return_val_if_fail (GST_IS_VDP_BUFFER_POOL (bpool), NULL);
+
+ priv = bpool->priv;
+ g_return_val_if_fail (priv->caps, NULL);
+
+ g_mutex_lock (priv->mutex);
+
+ buf = g_queue_pop_head (priv->buffers);
+ if (!buf) {
+ GstVdpBufferPoolClass *bpool_class = GST_VDP_BUFFER_POOL_GET_CLASS (bpool);
+
+ buf = bpool_class->alloc_buffer (bpool, error);
+ if (!buf)
+ goto done;
+ gst_buffer_set_caps (GST_BUFFER_CAST (buf), priv->caps);
+ gst_vdp_buffer_set_buffer_pool (buf, bpool);
+ }
+
+done:
+ g_mutex_unlock (priv->mutex);
+ return buf;
+}
+
+void
+gst_vdp_buffer_pool_set_max_buffers (GstVdpBufferPool * bpool,
+ guint max_buffers)
+{
+ GstVdpBufferPoolPrivate *priv;
+
+ g_return_if_fail (GST_IS_VDP_BUFFER_POOL (bpool));
+ g_return_if_fail (max_buffers >= -1);
+
+ priv = bpool->priv;
+
+ g_mutex_lock (priv->mutex);
+
+ if (max_buffers != -1) {
+ while (max_buffers < priv->buffers->length) {
+ GstVdpBuffer *buf;
+
+ buf = g_queue_pop_tail (priv->buffers);
+ gst_vdp_buffer_unref (buf);
+ }
+ }
+
+ priv->max_buffers = max_buffers;
+
+ g_mutex_unlock (priv->mutex);
+}
+
+guint
+gst_vdp_buffer_pool_get_max_buffers (GstVdpBufferPool * bpool)
+{
+ g_return_val_if_fail (GST_IS_VDP_BUFFER_POOL (bpool), 0);
+
+ return bpool->priv->max_buffers;
+}
+
+void
+gst_vdp_buffer_pool_set_caps (GstVdpBufferPool * bpool, const GstCaps * caps)
+{
+ GstVdpBufferPoolPrivate *priv;
+ GstVdpBufferPoolClass *bpool_class;
+ gboolean clear_bufs;
+
+ g_return_if_fail (GST_IS_VDP_BUFFER_POOL (bpool));
+ g_return_if_fail (GST_IS_CAPS (caps));
+
+ priv = bpool->priv;
+ bpool_class = GST_VDP_BUFFER_POOL_GET_CLASS (bpool);
+
+ g_mutex_lock (priv->mutex);
+
+ if (!bpool_class->set_caps (bpool, caps, &clear_bufs))
+ goto invalid_caps;
+
+ if (clear_bufs)
+ gst_vdp_buffer_pool_clear (bpool);
+
+ if (priv->caps)
+ gst_caps_unref (priv->caps);
+
+ priv->caps = gst_caps_copy (caps);
+
+done:
+ g_mutex_unlock (priv->mutex);
+ return;
+
+invalid_caps:
+ GST_WARNING ("Subclass didn't accept caps: %" GST_PTR_FORMAT, caps);
+ goto done;
+}
+
+const GstCaps *
+gst_vdp_buffer_pool_get_caps (GstVdpBufferPool * bpool)
+{
+ g_return_val_if_fail (GST_IS_VDP_BUFFER_POOL (bpool), NULL);
+
+ return bpool->priv->caps;
+}
+
+GstVdpDevice *
+gst_vdp_buffer_pool_get_device (GstVdpBufferPool * bpool)
+{
+ g_return_val_if_fail (GST_IS_VDP_BUFFER_POOL (bpool), NULL);
+
+ return bpool->priv->device;
+}
+
+static void
+gst_vdp_buffer_pool_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstVdpBufferPool *bpool = (GstVdpBufferPool *) object;
+ GstVdpBufferPoolPrivate *priv = bpool->priv;
+
+ switch (prop_id) {
+ case PROP_DEVICE:
+ g_value_set_object (value, priv->device);
+ break;
+
+ case PROP_CAPS:
+ g_value_set_pointer (value, priv->caps);
+ break;
+
+ case PROP_MAX_BUFFERS:
+ g_value_set_uint (value, priv->max_buffers);
+ break;
+
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_vdp_buffer_pool_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstVdpBufferPool *bpool = (GstVdpBufferPool *) object;
+ GstVdpBufferPoolPrivate *priv = bpool->priv;
+
+ switch (prop_id) {
+ case PROP_DEVICE:
+ priv->device = g_value_get_object (value);
+ break;
+
+ case PROP_CAPS:
+ gst_vdp_buffer_pool_set_caps (bpool, g_value_get_pointer (value));
+ break;
+
+ case PROP_MAX_BUFFERS:
+ gst_vdp_buffer_pool_set_max_buffers (bpool, g_value_get_uint (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_vdp_buffer_pool_finalize (GObject * object)
+{
+ GstVdpBufferPool *bpool = GST_VDP_BUFFER_POOL (object);
+ GstVdpBufferPoolPrivate *priv = bpool->priv;
+
+ g_mutex_free (priv->mutex);
+
+ if (priv->caps)
+ gst_caps_unref (priv->caps);
+
+ G_OBJECT_CLASS (gst_vdp_buffer_pool_parent_class)->finalize (object);
+}
+
+static void
+gst_vdp_buffer_pool_init (GstVdpBufferPool * bpool)
+{
+ GstVdpBufferPoolPrivate *priv;
+
+ bpool->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (bpool,
+ GST_TYPE_VDP_BUFFER_POOL, GstVdpBufferPoolPrivate);
+
+ priv->buffers = g_queue_new ();
+ priv->mutex = g_mutex_new ();
+
+ /* properties */
+ priv->caps = NULL;
+ priv->max_buffers = DEFAULT_MAX_BUFFERS;
+}
+
+static void
+gst_vdp_buffer_pool_class_init (GstVdpBufferPoolClass * bpool_klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (bpool_klass);
+
+ g_type_class_add_private (bpool_klass, sizeof (GstVdpBufferPoolPrivate));
+
+ object_class->get_property = gst_vdp_buffer_pool_get_property;
+ object_class->set_property = gst_vdp_buffer_pool_set_property;
+
+ object_class->finalize = gst_vdp_buffer_pool_finalize;
+
+ /**
+ * GstVdpBufferPool:device:
+ *
+ * The #GstVdpDevice this pool is bound to.
+ */
+ g_object_class_install_property
+ (object_class,
+ PROP_DEVICE,
+ g_param_spec_object ("device",
+ "Device",
+ "The GstVdpDevice this pool is bound to",
+ GST_TYPE_VDP_DEVICE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * GstVdpBufferPool:caps:
+ *
+ * The video object capabilities represented as a #GstCaps. This
+ * shall hold at least the "width" and "height" properties.
+ */
+ g_object_class_install_property
+ (object_class,
+ PROP_CAPS,
+ g_param_spec_pointer ("caps",
+ "Caps", "The buffer capabilities", G_PARAM_READWRITE));
+
+ /**
+ * GstVdpBufferPool:max-buffers:
+ *
+ * The maximum number of buffer in the pool. Or -1, the pool
+ * will hold as many objects as possible.
+ */
+ g_object_class_install_property
+ (object_class,
+ PROP_MAX_BUFFERS,
+ g_param_spec_int ("max-buffers",
+ "Max Buffers",
+ "The maximum number of buffers in the pool, or -1 for unlimited",
+ -1, G_MAXINT32, DEFAULT_MAX_BUFFERS, G_PARAM_READWRITE));
+}
diff --git a/sys/vdpau/gstvdp/gstvdpbufferpool.h b/sys/vdpau/gstvdp/gstvdpbufferpool.h
new file mode 100644
index 000000000..4f3d92c68
--- /dev/null
+++ b/sys/vdpau/gstvdp/gstvdpbufferpool.h
@@ -0,0 +1,74 @@
+/*
+ * GStreamer
+ * Copyright (C) 2009 Carl-Anton Ingmarsson <ca.ingmarsson@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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _GST_VDP_BUFFER_POOL_H_
+#define _GST_VDP_BUFFER_POOL_H_
+
+#include <gst/gst.h>
+
+typedef struct _GstVdpBufferPool GstVdpBufferPool;
+
+#include "gstvdpdevice.h"
+#include "gstvdpbuffer.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_VDP_BUFFER_POOL (gst_vdp_buffer_pool_get_type ())
+#define GST_VDP_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VDP_BUFFER_POOL, GstVdpBufferPool))
+#define GST_VDP_BUFFER_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VDP_BUFFER_POOL, GstVdpBufferPoolClass))
+#define GST_IS_VDP_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VDP_BUFFER_POOL))
+#define GST_IS_VDP_BUFFER_POOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VDP_BUFFER_POOL))
+#define GST_VDP_BUFFER_POOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_VDP_BUFFER_POOL, GstVdpBufferPoolClass))
+
+typedef struct _GstVdpBufferPoolClass GstVdpBufferPoolClass;
+typedef struct _GstVdpBufferPoolPrivate GstVdpBufferPoolPrivate;
+
+struct _GstVdpBufferPool
+{
+ GObject object;
+
+ GstVdpBufferPoolPrivate *priv;
+};
+
+struct _GstVdpBufferPoolClass
+{
+ GObjectClass object_class;
+
+ GstVdpBuffer *(*alloc_buffer) (GstVdpBufferPool *bpool, GError **error);
+ gboolean (*set_caps) (GstVdpBufferPool *bpool, const GstCaps *caps, gboolean *clear_bufs);
+ gboolean (*check_caps) (GstVdpBufferPool *bpool, const GstCaps *caps);
+};
+
+gboolean gst_vdp_buffer_pool_put_buffer (GstVdpBufferPool *bpool, GstVdpBuffer *buf);
+GstVdpBuffer *gst_vdp_buffer_pool_get_buffer (GstVdpBufferPool * bpool, GError **error);
+
+void gst_vdp_buffer_pool_set_max_buffers (GstVdpBufferPool *bpool, guint max_buffers);
+guint gst_vdp_buffer_pool_get_max_buffers (GstVdpBufferPool *bpool);
+
+void gst_vdp_buffer_pool_set_caps (GstVdpBufferPool *bpool, const GstCaps *caps);
+const GstCaps *gst_vdp_buffer_pool_get_caps (GstVdpBufferPool * bpool);
+
+GstVdpDevice *gst_vdp_buffer_pool_get_device (GstVdpBufferPool * bpool);
+
+GType gst_vdp_buffer_pool_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* _GST_VDP_BUFFER_POOL_H_ */