From 38451719700c630390cc8e78341a25d8e8dca38e Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Thu, 21 Aug 2008 15:19:00 -0400 Subject: [PATCH] First functional version of gstfs, with hardcoded stuff everywhere --- Makefile | 30 +++++++++ gstfs.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ xcode.c | 128 ++++++++++++++++++++++++++++++++++++ xcode.h | 6 ++ 4 files changed, 388 insertions(+) create mode 100644 Makefile create mode 100644 gstfs.c create mode 100644 xcode.c create mode 100644 xcode.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d07fe7d --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +VERSION=1.0 +DISTNAME=gstfs-$(VERSION) +DISTFILES=*.[ch] Makefile README COPYING + +SRCS=gstfs.c xcode.c +OBJS=$(SRCS:.c=.o) + +LIBS=-lpthread `pkg-config --libs fuse glib-2.0 gstreamer-0.10` + +CFLAGS+=-g -Wall -D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=26 `pkg-config --cflags fuse glib-2.0 gstreamer-0.10` + +all: gstfs + +gstfs: $(OBJS) + gcc -o gstfs $(OBJS) $(LIBS) + +clean: + $(RM) gstfs *.o + +dist: clean + mkdir $(DISTNAME) + cp $(DISTFILES) $(DISTNAME) + tar czvf $(DISTNAME).tar.gz $(DISTNAME) + $(RM) -r $(DISTNAME) + +distcheck: dist + mkdir build + cd build && tar xzvf ../$(DISTNAME).tar.gz && \ + cd $(DISTNAME) && $(MAKE) + $(RM) -r build diff --git a/gstfs.c b/gstfs.c new file mode 100644 index 0000000..c3602dc --- /dev/null +++ b/gstfs.c @@ -0,0 +1,224 @@ +/* + * gstfs - a gstreamer filesystem + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xcode.h" + +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)>(b)?(a):(b)) + +static GHashTable *file_cache; +static char *get_source_path(const char *filename); + +char *source_mount = "ogg"; +char *src_ext = "ogg", *dest_ext = "mp3"; + +// This stuff is stored into a hash table blah blah +struct file_info +{ + char *filename; /* hash key */ + char *source_filename; /* filename in other mount */ + size_t len; /* size of file */ + size_t alloc_len; /* allocated size of buf */ + char *buf; /* completely converted file */ +}; + +struct file_info *new_file_info(const char *filename) +{ + struct file_info *fi; + + fi = calloc(1, sizeof(struct file_info)); + fi->filename = strdup(filename); + fi->source_filename = get_source_path(filename); + return fi; +} + +char *replace_ext(char *filename, char *search, char *replace) +{ + char *ext = strrchr(filename, '.'); + if (ext && strcmp(ext+1, search) == 0) + { + *(ext+1) = 0; + filename = g_strconcat(filename, replace, NULL); + } + return filename; +} + +int is_target_type(const char *filename) +{ + char *ext = strrchr(filename, '.'); + return (ext && strcmp(ext+1, dest_ext) == 0); +} + +/* + * If the path represents a file in the mirror filesystem, then + * look for it in the cache. If not, create a new file info. + * + * If it isn't a mirror file, return NULL. + */ +static struct file_info *gstfs_lookup(const char *path) +{ + struct file_info *ret; + + if (!is_target_type(path)) + return NULL; + + ret = g_hash_table_lookup(file_cache, path); + if (!ret) + { + ret = new_file_info(path); + if (!ret) + goto out; + + g_hash_table_replace(file_cache, ret->filename, ret); + } +out: + return ret; +} + +static char *get_source_path(const char *filename) +{ + char *source; + + printf("filename: %s\n", filename); + + source = g_strdup_printf("%s%s", source_mount, filename); + source = replace_ext(source, dest_ext, src_ext); + printf("-> source filename: %s\n", source); + return source; +} + +int gstfs_statfs(const char *path, struct statvfs *buf) +{ + char *source_path; + + source_path = get_source_path(path); + if (statvfs(path, buf)) + return -errno; + + g_free(source_path); + return 0; +} + +int gstfs_getattr(const char *path, struct stat *stbuf) +{ + int ret = 0; + char *source_path; + struct file_info *converted; + + source_path = get_source_path(path); + + printf("in gettattr, foo\n"); + if (stat(source_path, stbuf)) + ret = -errno; + else if ((converted = gstfs_lookup(path))) + { + stbuf->st_size = converted->len; + } + + g_free(source_path); + return ret; +} + +static int read_cb(char *buf, size_t size, void *data) +{ + struct file_info *info = (struct file_info *) data; + + size_t newsz = info->len + size; + + if (info->alloc_len < newsz) + { + info->alloc_len = max(info->alloc_len * 2, newsz); + info->buf = realloc(info->buf, info->alloc_len); + if (!info->buf) + return -ENOMEM; + } + + memcpy(&info->buf[info->len], buf, size); + info->len += size; + return 0; +} + +int gstfs_read(const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *fi) +{ + struct file_info *info = gstfs_lookup(path); + size_t count; + + if (!info) + return -ENOENT; + + if (!info->buf) + transcode(info->source_filename, read_cb, info); + + if (info->len <= offset) + return 0; + + count = min(info->len - offset, size); + + memcpy(buf, &info->buf[offset], count); + return count; +} + +int gstfs_open(const char *path, struct fuse_file_info *fi) +{ + struct file_info *info = gstfs_lookup(path); + if (!info) + return -ENOENT; + + return 0; +} + +/** + * readdir - copy all entries from source mount + */ +int gstfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi) +{ + struct dirent *dirent; + DIR *dir; + char *source_path; + + source_path = get_source_path(path); + dir = opendir(source_path); + + if (!dir) + return -ENOENT; + + while ((dirent = readdir(dir))) + { + char *s = g_strdup(dirent->d_name); + s = replace_ext(s, src_ext, dest_ext); + filler(buf, s, NULL, 0); + + g_free(s); + } + closedir(dir); + + return 0; +} + +static struct fuse_operations gstfs_ops = { + .readdir = gstfs_readdir, + .statfs = gstfs_statfs, + .getattr = gstfs_getattr, + .open = gstfs_open, + .read = gstfs_read +}; + +int main(int argc, char *argv[]) +{ + gst_init (&argc, &argv); + file_cache = g_hash_table_new(g_str_hash, g_str_equal); + + return fuse_main(argc, argv, &gstfs_ops, NULL); +} diff --git a/xcode.c b/xcode.c new file mode 100644 index 0000000..4def741 --- /dev/null +++ b/xcode.c @@ -0,0 +1,128 @@ +/* + * gstfs - gstreamer glue routines for transcoding + */ + +#include +#include +#include +#include +#include + +struct pipe_params +{ + int fd; + void (*add_data_cb)(char *, size_t, void *); + void *user_data; +}; + + +/* +static gboolean bus_call (GstBus *bus, GstMessage *msg, gpointer data) +{ + gchar *debug; + GError *error; + done_cb_t callback = (done_cb_t) data; + + switch (GST_MESSAGE_TYPE (msg)) { + + case GST_MESSAGE_EOS: + g_main_loop_quit (loop); + break; + + case GST_MESSAGE_ERROR: + gst_message_parse_error (msg, &error, &debug); + g_free (debug); + + g_printerr ("Error: %s\n", error->message); + g_error_free (error); + + g_main_loop_quit (loop); + break; + + default: + break; + } + return TRUE; +} +*/ + +void close_pipe(void *data) +{ + int fd = (int) data; + printf("close pipe! %d\n", fd); + close(fd); +} + +void *send_pipe(void *data) +{ + struct pipe_params *param = (struct pipe_params *) data; + char buf[PIPE_BUF]; + size_t sizeread; + + while ((sizeread = read(param->fd, buf, sizeof(buf))) > 0) + { + param->add_data_cb(buf, sizeread, param->user_data); + } + return NULL; +} + +/* + * Transcodes a file into a buffer, blocking until done. + */ +int transcode(char *filename, void (*add_data_cb)(char *, size_t, void *), + void *user_data) +{ + GstElement *pipeline, *source, *dest; + GError *error = NULL; + GstBus *bus; + char *pipeline_str = "filesrc name=\"_source\" ! oggdemux ! vorbisdec ! audioconvert ! wavenc ! fdsink name=\"_dest\" sync=false"; + int pipefds[2]; + + struct pipe_params thread_params; + pthread_t thread; + void *thread_status; + + pipeline = gst_parse_launch(pipeline_str, &error); + if (error) + { + fprintf(stderr, "Error parsing pipeline: %s\n", error->message); + return -1; + } + + source = gst_bin_get_by_name(GST_BIN(pipeline), "_source"); + dest = gst_bin_get_by_name(GST_BIN(pipeline), "_dest"); + + if (!pipeline || !source || !dest) + { + fprintf(stderr, "Could not initialize pipeline\n"); + return -2; + } + + if (pipe(pipefds)) + { + perror("gstfs"); + return -1; + } + + thread_params.fd = pipefds[0]; + thread_params.add_data_cb = add_data_cb; + thread_params.user_data = user_data; + + pthread_create(&thread, NULL, send_pipe, (void *) &thread_params); + + g_object_set(G_OBJECT(source), "location", filename, NULL); + g_object_set(G_OBJECT(dest), "fd", pipefds[1], NULL); + + bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); + gst_bus_add_signal_watch(bus); + gst_element_set_state(pipeline, GST_STATE_PLAYING); + GstMessage *message = gst_bus_poll(bus, GST_MESSAGE_EOS | + GST_MESSAGE_ERROR, -1); + gst_message_unref(message); + + // close read-side so pipe will terminate + close(pipefds[1]); + pthread_join(&thread, thread_status); + + return 0; +} diff --git a/xcode.h b/xcode.h new file mode 100644 index 0000000..7d7d4e7 --- /dev/null +++ b/xcode.h @@ -0,0 +1,6 @@ +#ifndef _XCODE_H +#define _XCODE_H + +extern int transcode(char *filename, int (*add_data_cb)(char *, size_t, void *), void *user_data); + +#endif /* _XCODE_H */ -- 2.11.4.GIT