Implement working caching.
authorIlpo Ruotsalainen <ilpo.ruotsalainen@movial.fi>
Fri, 17 Oct 2008 11:46:46 +0000 (14:46 +0300)
committerIlpo Ruotsalainen <ilpo.ruotsalainen@movial.fi>
Fri, 17 Oct 2008 11:46:46 +0000 (14:46 +0300)
isatis-player/isatissrc.c [new file with mode: 0644]
isatis-player/isatissrc.h [new file with mode: 0644]
isatis-plugin/plugin.c

diff --git a/isatis-player/isatissrc.c b/isatis-player/isatissrc.c
new file mode 100644 (file)
index 0000000..c6f9d13
--- /dev/null
@@ -0,0 +1,295 @@
+#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",
+               "");
diff --git a/isatis-player/isatissrc.h b/isatis-player/isatissrc.h
new file mode 100644 (file)
index 0000000..f70082f
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef __GST_ISATIS_SRC_H__
+#define __GST_ISATIS_SRC_H__
+
+#include <gst/base/gstpushsrc.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_ISATIS_SRC \
+         (gst_isatis_src_get_type())
+#define GST_ISATIS_SRC(obj) \
+         (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ISATIS_SRC,GstIsatisSrc))
+#define GST_ISATIS_SRC_CLASS(klass) \
+         (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ISATIS_SRC,GstIsatisSrcClass))
+#define GST_IS_ISATIS_SRC(obj) \
+         (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ISATIS_SRC))
+#define GST_IS_ISATIS_SRC_CLASS(klass) \
+         (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ISATIS_SRC))
+
+typedef struct _GstIsatisSrc      GstIsatisSrc;
+typedef struct _GstIsatisSrcClass GstIsatisSrcClass;
+
+struct _GstIsatisSrc
+{
+       GstPushSrc element;
+};
+
+struct _GstIsatisSrcClass
+{
+       GstPushSrcClass parent_class;
+};
+
+GType gst_isatis_src_get_type(void);
+
+G_END_DECLS
+
+#endif
index 473f422..13e9a2b 100644 (file)
@@ -18,6 +18,7 @@
 #define SUPPORTED_MIME_TYPES \
        "application/ogg:ogg:OGG Multimedia;" \
        "audio/ogg:ogg:OGG Audio;" \
+       "video/mp4:mp4:MPEG-4 Video;" \
        "video/mpeg:mpg, mpeg, mpe:MPEG Video;" \
        "video/quicktime:mov:QuickTime Video;" \
        "video/x-ms-asf:asf:Advanced Systems Format;" \
@@ -78,7 +79,7 @@ static NPError NPP_SpawnPlayer(NPP instance)
                                        uri = priv->uri;
                                else
                                {
-                                       snprintf(uri_buf, sizeof(uri_buf), "fd://%d", pipe_fds[0]);
+                                       snprintf(uri_buf, sizeof(uri_buf), "isatis://%d", pipe_fds[0]);
 
                                        uri = uri_buf;
                                }
@@ -314,7 +315,7 @@ NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value)
        switch (variable)
        {
                case NPPVpluginNameString:
-                       *((char **)value) = "Isatis Multimedia Player";
+                       *((char **)value) = "Isatis Multimedia Player (compatible with QuickTime 7)";
                        return NPERR_NO_ERROR;
 
                case NPPVpluginDescriptionString: