--- /dev/null
+#include "isatissrc.h"
+
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+
+#define CACHE_BUFFER_SIZE 4096
+
+GST_DEBUG_CATEGORY_STATIC(isatissrc_debug);
+#define GST_CAT_DEFAULT isatissrc_debug
+
+static GStaticMutex isatis_mutex = G_STATIC_MUTEX_INIT;
+/* These are protected by the mutex and accessed from all threads */
+static gchar *isatis_uri = NULL;
+static gboolean isatis_cache_done = FALSE;
+
+/* These never change once set and don't need mutex protection */
+static gint isatis_input_fd = -1;
+static gint isatis_cache_write_fd = -1;
+static gint isatis_cache_read_fd = -1;
+
+static int gst_isatis_src_make_cache_file(void)
+{
+ char tmp_file[] = "/tmp/isatis.XXXXXX";
+
+ mktemp(tmp_file);
+
+ isatis_cache_write_fd = open(tmp_file, O_CREAT | O_EXCL | O_WRONLY, S_IRWXU);
+ if (isatis_cache_write_fd < 0)
+ {
+ GST_ERROR("failed to open cache file for writing: %s", g_strerror(errno));
+ return -1;
+ }
+
+ isatis_cache_read_fd = open(tmp_file, O_RDONLY);
+ if (isatis_cache_read_fd < 0)
+ {
+ unlink(tmp_file);
+ close(isatis_cache_write_fd);
+ isatis_cache_write_fd = -1;
+ GST_ERROR("failed to open cache file for reading: %s", g_strerror(errno));
+ return -1;
+ }
+
+ unlink(tmp_file);
+
+ return 0;
+}
+
+static gpointer gst_isatis_src_cache_thread_func(gpointer unused)
+{
+ int r;
+ char buffer[CACHE_BUFFER_SIZE];
+
+ GST_DEBUG("cache thread started");
+
+ do
+ {
+ r = read(isatis_input_fd, buffer, CACHE_BUFFER_SIZE);
+
+ if (r > 0)
+ write(isatis_cache_write_fd, buffer, r); /* XXX handle partials and failures XXX */
+ }
+ while (r > 0);
+
+ g_static_mutex_lock(&isatis_mutex);
+ isatis_cache_done = TRUE;
+ g_static_mutex_unlock(&isatis_mutex);
+
+ GST_DEBUG("cache thread exiting");
+
+ return NULL;
+}
+
+static const GstElementDetails gst_isatis_src_details =
+ GST_ELEMENT_DETAILS("Isatis plugin source",
+ "Source/File",
+ "Read from Isatis browser plugin",
+ "Ilpo Ruotsalainen <ilpo.ruotsalainen@movial.fi>");
+
+static GstStaticPadTemplate srctemplate =
+ GST_STATIC_PAD_TEMPLATE("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+static GstURIType gst_isatis_src_uri_get_type(void)
+{
+ return GST_URI_SRC;
+}
+
+static gchar **gst_isatis_src_uri_get_protocols(void)
+{
+ static gchar *protocols[] = { "isatis", NULL };
+
+ return protocols;
+}
+
+static const gchar *gst_isatis_src_uri_get_uri(GstURIHandler *handler)
+{
+ /* XXX This isn't really threadsafe, and relies on the URI never changing once set XXX */
+
+ return isatis_uri;
+}
+
+static gboolean gst_isatis_src_uri_set_uri(GstURIHandler *handler, const gchar *uri)
+{
+ GstIsatisSrc *src = GST_ISATIS_SRC(handler);
+ gboolean ret = TRUE;
+ gint fd;
+
+ GST_DEBUG_OBJECT(src, "setting URI to %s", uri);
+
+ if (sscanf(uri, "isatis://%d", &fd) != 1 || fd < 0)
+ return FALSE;
+
+ g_static_mutex_lock(&isatis_mutex);
+
+ if (!isatis_uri)
+ {
+ GError *error = NULL;
+
+ isatis_uri = g_strdup(uri);
+ isatis_input_fd = fd;
+
+ if (gst_isatis_src_make_cache_file() < 0)
+ {
+ GST_ELEMENT_ERROR(src, RESOURCE, OPEN_WRITE,
+ ("Could not open cache file: %s", g_strerror(errno)), NULL);
+ }
+ else if (!g_thread_create(gst_isatis_src_cache_thread_func, NULL, FALSE, &error))
+ {
+ GST_ELEMENT_ERROR(src, CORE, THREAD,
+ ("Could not create cache thread: %s", error->message), NULL);
+
+ g_error_free(error);
+ }
+ }
+ else if (strcmp(isatis_uri, uri))
+ ret = FALSE; /* Cannot change URI once it has been set */
+
+ g_static_mutex_unlock(&isatis_mutex);
+
+ return ret;
+}
+
+static void gst_isatis_src_uri_handler_init(gpointer g_iface, gpointer iface_data)
+{
+ GstURIHandlerInterface *iface = (GstURIHandlerInterface *)g_iface;
+
+ iface->get_type = gst_isatis_src_uri_get_type;
+ iface->get_protocols = gst_isatis_src_uri_get_protocols;
+ iface->get_uri = gst_isatis_src_uri_get_uri;
+ iface->set_uri = gst_isatis_src_uri_set_uri;
+}
+
+static void _do_init(GType isatissrc_type)
+{
+ static const GInterfaceInfo urihandler_info =
+ {
+ gst_isatis_src_uri_handler_init,
+ NULL,
+ NULL
+ };
+
+ g_type_add_interface_static(isatissrc_type, GST_TYPE_URI_HANDLER, &urihandler_info);
+ GST_DEBUG_CATEGORY_INIT(isatissrc_debug, "isatissrc", 0, "Isatis Source");
+}
+
+GST_BOILERPLATE_FULL(GstIsatisSrc, gst_isatis_src, GstPushSrc, GST_TYPE_PUSH_SRC, _do_init);
+
+static void gst_isatis_src_base_init(gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
+ static const GstElementDetails details =
+ GST_ELEMENT_DETAILS(
+ "Isatis plugin source",
+ "Source",
+ "Read from Isatis plugin",
+ "Ilpo Ruotsalainen <ilpo.ruotsalainen@movial.fi>");
+
+ gst_element_class_set_details(element_class, &details);
+ gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&srctemplate));
+}
+
+static gboolean gst_isatis_src_start(GstBaseSrc *bsrc)
+{
+ GstIsatisSrc *src = GST_ISATIS_SRC(bsrc);
+
+ GST_DEBUG_OBJECT(src, "starting");
+
+ if (isatis_cache_read_fd != -1)
+ {
+ if (lseek(isatis_cache_read_fd, 0, SEEK_SET) < 0)
+ {
+ GST_ELEMENT_ERROR(src, RESOURCE, SEEK, (NULL), GST_ERROR_SYSTEM);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static GstFlowReturn gst_isatis_src_create(GstPushSrc *psrc, GstBuffer **outbuf)
+{
+ GstIsatisSrc *src = GST_ISATIS_SRC(psrc);
+ GstBuffer *buf;
+ guint blocksize = GST_BASE_SRC(src)->blocksize;
+ gssize readbytes;
+ gboolean cache_done;
+
+ if (isatis_cache_read_fd == -1)
+ return GST_FLOW_WRONG_STATE;
+
+ buf = gst_buffer_new_and_alloc(blocksize);
+
+retry:
+ g_static_mutex_lock(&isatis_mutex);
+ cache_done = isatis_cache_done;
+ g_static_mutex_unlock(&isatis_mutex);
+
+ readbytes = read(isatis_cache_read_fd, GST_BUFFER_DATA(buf), blocksize);
+
+ if (readbytes < 0)
+ {
+ GST_ELEMENT_ERROR(src, RESOURCE, READ, (NULL), ("reading cache file failed: %s", g_strerror(errno)));
+ gst_buffer_unref(buf);
+ return GST_FLOW_ERROR;
+ }
+ else if (readbytes == 0)
+ {
+ if (cache_done)
+ {
+ /* Actual EOS */
+ GST_DEBUG_OBJECT(src, "No more data, EOS");
+
+ gst_buffer_unref(buf);
+ return GST_FLOW_UNEXPECTED;
+ }
+ else
+ {
+ /* Need to pull more data from stream */
+ GST_DEBUG_OBJECT(src, "Waiting for more data");
+
+ sleep(1);
+ goto retry;
+ }
+ }
+
+ /* Got some data */
+
+ GST_DEBUG_OBJECT(src, "read %" G_GSSIZE_FORMAT " bytes", readbytes);
+
+ GST_BUFFER_SIZE(buf) = readbytes;
+ GST_BUFFER_OFFSET(buf) = GST_BUFFER_OFFSET_NONE; /* XXX */
+ GST_BUFFER_TIMESTAMP(buf) = GST_CLOCK_TIME_NONE;
+
+ *outbuf = buf;
+
+ return GST_FLOW_OK;
+}
+
+static void gst_isatis_src_class_init(GstIsatisSrcClass *klass)
+{
+ GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS(klass);
+ GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS(klass);
+
+ gstbasesrc_class->start = GST_DEBUG_FUNCPTR(gst_isatis_src_start);
+
+ gstpushsrc_class->create = GST_DEBUG_FUNCPTR(gst_isatis_src_create);
+}
+
+static void gst_isatis_src_init(GstIsatisSrc *src, GstIsatisSrcClass *klass)
+{
+ GST_DEBUG_OBJECT(src, "initializing");
+}
+
+static gboolean gst_isatis_plugin_init(GstPlugin *plugin)
+{
+ return gst_element_register(plugin, "isatissrc", GST_RANK_PRIMARY, gst_isatis_src_get_type());
+}
+
+/* Sigh. */
+#define PACKAGE "isatis"
+
+GST_PLUGIN_DEFINE_STATIC(
+ GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "isatisplugin",
+ "Isatis internal plugin",
+ gst_isatis_plugin_init,
+ VERSION,
+ "GPL",
+ "Isatis",
+ "");