Mercurial > audlegacy-plugins
changeset 1726:0a689ca43d7a
Automated merge with ssh://hg.atheme.org//hg/audacious-plugins
author | William Pitcock <nenolod@atheme.org> |
---|---|
date | Tue, 18 Sep 2007 09:26:05 -0500 |
parents | f9856ca98943 (current diff) 655949f889ec (diff) |
children | 9d6de95dd7ed |
files | |
diffstat | 11 files changed, 1716 insertions(+), 19 deletions(-) [+] |
line wrap: on
line diff
--- a/configure.ac Tue Sep 18 09:25:56 2007 -0500 +++ b/configure.ac Tue Sep 18 09:26:05 2007 -0500 @@ -447,6 +447,27 @@ GENERAL_PLUGINS="$GENERAL_PLUGINS hotkey" fi +dnl *** Gnome Shortcuts Plugin + +AC_ARG_ENABLE(gnomeshortcuts, + [ --disable-gnomeshortcuts disable gnome shortcuts (default=enabled)], + [enable_gnomeshortcuts=$enableval], + [enable_gnomeshortcuts="yes"] +) + +if test "x$enable_gnomeshortcuts" = "xyes"; then + have_gnomeshortcuts="yes" + PKG_CHECK_MODULES(DBUS, [dbus-1 >= 0.60 dbus-glib-1 >= 0.60],, + [AC_MSG_ERROR([Cannot find dbus-glib >= 0.60 for gnomeshortcuts plugin])]) +else + AC_MSG_RESULT([*** Gnome Shortcuts Plugin disabled per user request ***]) + have_gnomeshortcuts="no" +fi + +if test "x$have_gnomeshortcuts" = "xyes"; then + GENERAL_PLUGINS="$GENERAL_PLUGINS gnomeshortcuts" +fi + dnl *** Status Icon plugin (for X11 only) AC_ARG_ENABLE(statusicon, @@ -1253,6 +1274,24 @@ dnl *** End of Scrobbler checks *** +dnl *** neon http plugin *** + +AC_ARG_ENABLE(neon, +[ --enable-neon enable neon support (experimental). (default=disabled)], +[have_neon=$enableval], +[have_neon=no]) + +if test "x$have_neon" = "xyes"; then + PKG_CHECK_MODULES(NEON, [neon >= 0.27], [TRANSPORT_PLUGINS="$TRANSPORT_PLUGINS neon" ], [have_neon="no"]) + NEON_LIBS=`pkg-config --libs neon` + NEON_CFLAGS=`pkg-config --cflags neon` +else + have_neon=no +fi +AC_SUBST(NEON_LIBS) +AC_SUBST(NEON_CLFAGS) + + dnl *** MMS AC_ARG_ENABLE(mms, @@ -1563,6 +1602,7 @@ echo " -> X Composite support: $have_aosd_xcomp" echo " Control via event device (evdev-plug): $have_evdevplug" echo " Global Hotkey Plugin: $have_hotkey" +echo " Gnome Shortcuts Plugin: $have_gnomeshortcuts" echo " LIRC: $have_lirc" echo " AudioScrobbler Client: $scrobbler" echo " Upload to MTP device: $have_mtp_up" @@ -1589,6 +1629,7 @@ echo " ---------" echo " stdio transport: yes" echo " curl-based http/https: $have_curl" +echo " neon-based http/https: $have_neon" echo " libmms-based mms: $have_mms" echo " lastfm transport: $have_lastfm" echo
--- a/mk/rules.mk.in Tue Sep 18 09:25:56 2007 -0500 +++ b/mk/rules.mk.in Tue Sep 18 09:26:05 2007 -0500 @@ -332,6 +332,8 @@ CONFIGDB_BACKEND ?= @CONFIGDB_BACKEND@ CURL_CFLAGS ?= @CURL_CFLAGS@ CURL_LIBS ?= @CURL_LIBS@ +NEON_CFLAGS ?= @NEON_CFLAGS@ +NEON_LIBS ?= @NEON_LIBS@ CHARDET_LIBS ?= @CHARDET_LIBS@ SUBDIR_GUESS ?= @SUBDIR_GUESS@ LIBNOTIFY_CFLAGS ?= @LIBNOTIFY_CFLAGS@
--- a/src/arts/arts.c Tue Sep 18 09:25:56 2007 -0500 +++ b/src/arts/arts.c Tue Sep 18 09:26:05 2007 -0500 @@ -32,25 +32,23 @@ OutputPlugin arts_op = { - NULL, - NULL, - "aRts Output Plugin", - artsxmms_init, - NULL, - about, - artsxmms_configure, - artsxmms_get_volume, - artsxmms_set_volume, - artsxmms_open, - artsxmms_write, - artsxmms_close, - artsxmms_flush, - artsxmms_pause, - artsxmms_free, - artsxmms_playing, - artsxmms_get_output_time, - artsxmms_get_written_time, - artsxmms_tell_audio + .description = "aRts Output Plugin", + .init = artsxmms_init, + .cleanup = NULL, + .about = about, + .configure = artsxmms_configure, + .get_volume = artsxmms_get_volume, + .set_volume = artsxmms_set_volume, + .open_audio = artsxmms_open, + .write_audio = artsxmms_write, + .close_audio = artsxmms_close, + .flush = artsxmms_flush, + .pause = artsxmms_pause, + .buffer_free = artsxmms_free, + .buffer_playing = artsxmms_playing, + .output_time = artsxmms_get_output_time, + .written_time = artsxmms_get_written_time, + .tell_audio = artsxmms_tell_audio }; OutputPlugin *arts_oplist[] = { &arts_op, NULL };
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gnomeshortcuts/Makefile Tue Sep 18 09:26:05 2007 -0500 @@ -0,0 +1,16 @@ +include ../../mk/rules.mk +include ../../mk/init.mk + +OBJECTIVE_LIBS = libgnomeshortcuts$(SHARED_SUFFIX) + +LIBDIR = $(plugindir)/$(GENERAL_PLUGIN_DIR) + +LIBADD = $(GLIB_LIBS) $(DBUS_LIBS) +SOURCES = gnomeshortcuts.c + +OBJECTS = ${SOURCES:.c=.o} + +CFLAGS += $(PICFLAGS) $(GLIB_CFLAGS) $(DBUS_CFLAGS) \ + -I../../intl -I../.. -I.. + +include ../../mk/objective.mk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gnomeshortcuts/gnomeshortcuts.c Tue Sep 18 09:26:05 2007 -0500 @@ -0,0 +1,307 @@ +/* -*- Mode: C; indent-tabs: t; c-basic-offset: 9; tab-width: 9 -*- */ +/* + * This file is part of audacious-gnome-shortcut plugin for audacious + * + * Copyright (c) 2007 Sascha Hlusiak <contact@saschahlusiak.de> + * Name: plugin.c + * Description: plugin.c + * + * audacious-gnome-shortcut is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * audacious-gnome-shortcut 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with audacious-gnome-shortcut; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> + +#include <string.h> +#include <dbus/dbus.h> +#include <dbus/dbus-glib-bindings.h> +#include <glib-object.h> + +#include <audacious/plugin.h> +#include <audacious/auddrct.h> + +#include <audacious/i18n.h> +#include <audacious/util.h> + + +static void init (void); +static void about (void); +static void cleanup (void); +void gnome_remote_init(); +void gnome_remote_uninit(); + +static gboolean loaded = FALSE; +static DBusGProxy *media_player_keys_proxy = NULL; + +static GeneralPlugin audaciousgnomeshortcuts = +{ + .description = "Gnome Shortcuts", + .init = init, + .about = about, + .cleanup = cleanup +}; + +GeneralPlugin *gnomeshortcuts_gplist[] = { &audaciousgnomeshortcuts, NULL }; +SIMPLE_GENERAL_PLUGIN(gnomeshortcuts, gnomeshortcuts_gplist); + +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) + + +static void +hotkey_marshal_VOID__STRING_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__STRING_STRING) (gpointer data1, + gpointer arg_1, + gpointer arg_2); + register GMarshalFunc_VOID__STRING_STRING callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + } else { + data1 = g_value_peek_pointer (param_values + 0); + } + callback = (GMarshalFunc_VOID__STRING_STRING) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_string (param_values + 1), + g_marshal_value_peek_string (param_values + 2)); +} + +static void +on_media_player_key_pressed (DBusGProxy *proxy, const gchar *application, const gchar *key) +{ + if (strcmp ("Audacious", application) == 0) { + gint current_volume, old_volume; + static gint volume_static = 0; + gboolean play, mute; + + /* playing or not */ + play = audacious_drct_is_playing (); + + /* get current volume */ + audacious_drct_get_volume_main (¤t_volume); + old_volume = current_volume; + if (current_volume) + { + /* volume is not mute */ + mute = FALSE; + } else { + /* volume is mute */ + mute = TRUE; + } + + /* mute the playback */ + if (strcmp ("Mute", key) == 0) + { + if (!mute) + { + volume_static = current_volume; + audacious_drct_set_main_volume (0); + mute = TRUE; + } else { + audacious_drct_set_main_volume (volume_static); + mute = FALSE; + } + return; + } + + /* decreace volume */ +/* if ((keycode == plugin_cfg.vol_down) && (state == plugin_cfg.vol_down_mask)) + { + if (mute) + { + current_volume = old_volume; + old_volume = 0; + mute = FALSE; + } + + if ((current_volume -= plugin_cfg.vol_decrement) < 0) + { + current_volume = 0; + } + + if (current_volume != old_volume) + { + xmms_remote_set_main_volume (audacioushotkey.xmms_session, + current_volume); + } + + old_volume = current_volume; + return TRUE; + }*/ + + /* increase volume */ +/* if ((keycode == plugin_cfg.vol_up) && (state == plugin_cfg.vol_up_mask)) + { + if (mute) + { + current_volume = old_volume; + old_volume = 0; + mute = FALSE; + } + + if ((current_volume += plugin_cfg.vol_increment) > 100) + { + current_volume = 100; + } + + if (current_volume != old_volume) + { + xmms_remote_set_main_volume (audacioushotkey.xmms_session, + current_volume); + } + + old_volume = current_volume; + return TRUE; + }*/ + + /* play */ + if (strcmp ("Play", key) == 0) + { + if (!play) + { + audacious_drct_play (); + } else { + audacious_drct_pause (); + } + return; + } + + /* pause */ + if (strcmp ("Pause", key) == 0) + { + if (!play) audacious_drct_play (); + else audacious_drct_pause (); + + return; + } + + /* stop */ + if (strcmp ("Stop", key) == 0) + { + audacious_drct_stop (); + return; + } + + /* prev track */ + if (strcmp ("Previous", key) == 0) + { + audacious_drct_playlist_prev (); + return; + } + + /* next track */ + if (strcmp ("Next", key) == 0) + { + audacious_drct_playlist_next (); + return; + } + } +} + +void gnome_remote_uninit () +{ + GError *error = NULL; + if (media_player_keys_proxy == NULL) return; + + dbus_g_proxy_disconnect_signal (media_player_keys_proxy, "MediaPlayerKeyPressed", + G_CALLBACK (on_media_player_key_pressed), NULL); + + dbus_g_proxy_call (media_player_keys_proxy, + "ReleaseMediaPlayerKeys", &error, + G_TYPE_STRING, "Audacious", + G_TYPE_INVALID, G_TYPE_INVALID); + if (error != NULL) { + g_warning ("Could not release media player keys: %s", error->message); + g_error_free (error); + } + g_object_unref(media_player_keys_proxy); + media_player_keys_proxy = NULL; +} + +void gnome_remote_init () +{ + DBusGConnection *bus; + GError *error = NULL; + dbus_g_thread_init(); + + bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if ((bus == NULL) || error) { + g_warning ("Error connecting to DBus: %s", error->message); + } else { + media_player_keys_proxy = dbus_g_proxy_new_for_name (bus, + "org.gnome.SettingsDaemon", + "/org/gnome/SettingsDaemon", + "org.gnome.SettingsDaemon"); + if (media_player_keys_proxy == NULL) return; + + dbus_g_proxy_call (media_player_keys_proxy, + "GrabMediaPlayerKeys", &error, + G_TYPE_STRING, "Audacious", + G_TYPE_UINT, 0, + G_TYPE_INVALID, + G_TYPE_INVALID); + if (error != NULL) { + g_warning ("Could not release media player keys: %s", error->message); + g_error_free (error); + } + + dbus_g_object_register_marshaller (hotkey_marshal_VOID__STRING_STRING, + G_TYPE_NONE, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID); + + dbus_g_proxy_add_signal (media_player_keys_proxy, "MediaPlayerKeyPressed", + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID); + + dbus_g_proxy_connect_signal (media_player_keys_proxy, "MediaPlayerKeyPressed", + G_CALLBACK (on_media_player_key_pressed), NULL, NULL); + } +} + +static void about (void) +{ + static GtkWidget *dialog; + + dialog = audacious_info_dialog (_("About Gnome Shortcut Plugin"), + _("Gnome Shortcut Plugin\n" + "Let's you control the player with Gnome's shortcuts.\n\n" + "Copyright (C) 2007 Sascha Hlusiak <contact@saschahlusiak.de>\n\n" + ), + _("OK"), TRUE, NULL, NULL); + + gtk_signal_connect(GTK_OBJECT(dialog), "destroy", + GTK_SIGNAL_FUNC(gtk_widget_destroyed), &dialog); +} + +static void init (void) +{ + gnome_remote_init(); + loaded = TRUE; +} + +static void cleanup (void) +{ + if (!loaded) return; + gnome_remote_uninit(); + loaded = FALSE; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/neon/Makefile Tue Sep 18 09:26:05 2007 -0500 @@ -0,0 +1,24 @@ +include ../../mk/rules.mk +include ../../mk/init.mk + +SUBDIRS = + +OBJECTIVE_LIBS = libneonsrc$(SHARED_SUFFIX) + +LIBDIR = $(plugindir)/$(TRANSPORT_PLUGIN_DIR) + +LIBADD += $(GTK_LIBS) $(GLIB_LIBS) $(PANGO_LIBS) $(NEON_LIBS) + +SOURCES = neon.c rb.c + +CFLAGS += $(PICFLAGS) $(GTK_CFLAGS) $(GLIB_CFLAGS) $(PANGO_CFLAGS) $(BEEP_DEFINES) $(NEON_CFLAGS) -I../../intl -I../.. + +#CFLAGS += -Wpointer-arith -Wimplicit -Wnested-externs -Wcast-align \ +#-Wcast-qual -Wstrict-prototypes -Wmissing-prototypes \ +#-Wmissing-declarations -W -Wno-unused -Wshadow -Wmissing-noreturn \ +#-Wundef -Wpacked -Wnested-externs -Wbad-function-cast -Wredundant-decls \ +#-Wfloat-equal -Wdisabled-optimization -pedantic -std=c99 + +OBJECTS = ${SOURCES:.c=.o} + +include ../../mk/objective.mk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/neon/debug.h Tue Sep 18 09:26:05 2007 -0500 @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2005 Ralf Ertzinger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef DEBUG_H +#define DEBUG_H + +#include <stdio.h> + +#define _ENTER _DEBUG("enter") +#define _LEAVE _DEBUG("leave"); return +#define _MESSAGE(tag, string, ...) do { fprintf(stderr, "%s: libneonsrc.so: %s:%d (%s): " string "\n", \ + tag, __FILE__, __LINE__, __func__, ##__VA_ARGS__); } while(0) + +#define _ERROR(...) _MESSAGE("ERROR", __VA_ARGS__) + +#ifdef NEON_DEBUG +#define _DEBUG(...) _MESSAGE("DEBUG", __VA_ARGS__) +#else +#define _DEBUG(...) {} +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/neon/neon.c Tue Sep 18 09:26:05 2007 -0500 @@ -0,0 +1,883 @@ +#include <audacious/vfs.h> +#include <audacious/plugin.h> + +#include <ne_socket.h> +#include <ne_utils.h> +#include <ne_redirect.h> +#include <ne_request.h> + +#include "debug.h" +#include "neon.h" +#include "rb.h" + +#define NBUFSIZ (128u*1024u) +#define NETBLKSIZ (4096u) + +DECLARE_PLUGIN(neon, init, fini) + +VFSConstructor neon_http_const = { + "http://", + neon_vfs_fopen_impl, + neon_vfs_fclose_impl, + neon_vfs_fread_impl, + neon_vfs_fwrite_impl, + neon_vfs_getc_impl, + neon_vfs_ungetc_impl, + neon_vfs_fseek_impl, + neon_vfs_rewind_impl, + neon_vfs_ftell_impl, + neon_vfs_feof_impl, + neon_vfs_truncate_impl, + neon_vfs_fsize_impl, + neon_vfs_metadata_impl +}; + +/* + * ======== + */ + +static struct neon_handle* handle_init(void) { + + struct neon_handle* h; + + _ENTER; + + if (NULL == (h = malloc(sizeof(struct neon_handle)))) { + _ERROR("Could not allocate memory for handle"); + _LEAVE NULL; + } + + if (0 != init_rb(&(h->rb), NBUFSIZ)) { + _ERROR("Could not initialize buffer"); + free(h); + _LEAVE NULL; + } + + h->url = NULL; + h->purl = &purl; + memset(h->purl, 0, sizeof(ne_uri)); + h->session = NULL; + h->request = NULL; + h->redircount = 0; + h->pos = 0; + h->content_length = -1; + h->can_ranges = FALSE; + h->reader = NULL; + h->reader_status.mutex = g_mutex_new(); + h->reader_status.cond = g_cond_new(); + h->reader_status.reading = FALSE; + h->reader_status.status = NEON_READER_INIT; + h->eof = FALSE; + + _LEAVE h; +} + +/* + * ----- + */ + +static void handle_free(struct neon_handle* h) { + + _ENTER; + + ne_uri_free(h->purl); + destroy_rb(&h->rb); + free(h); + + _LEAVE; +} + +/* + * ---- + */ + +static void init(void) { + + int ret; + + _ENTER; + + if (0 != (ret = ne_sock_init())) { + _ERROR("Could not initialize neon library: %d\n", ret); + _LEAVE; + } + + vfs_register_transport(&neon_http_const); + + if (0 != ne_has_support(NE_FEATURE_SSL)) { + _DEBUG("neon compiled with thread-safe SSL, enabling https:// transport"); + } + + _LEAVE; +} + +/* + * ----- + */ + +static void fini(void) { + + _ENTER; + + ne_sock_exit(); + + _LEAVE; +} + +/* + * ----- + */ + +static void kill_reader(struct neon_handle* h) { + + _ENTER; + + _DEBUG("Signaling reader thread to terminate"); + g_mutex_lock(h->reader_status.mutex); + h->reader_status.reading = FALSE; + g_cond_signal(h->reader_status.cond); + g_mutex_unlock(h->reader_status.mutex); + + _DEBUG("Waiting for reader thread to die..."); + g_thread_join(h->reader); + _DEBUG("Reader thread has died"); + h->reader = NULL; +} + +/* + * ----- + */ + +static void handle_headers(struct neon_handle* h) { + + const gchar* name; + const gchar* value; + void* cursor = NULL; + long len; + gchar* endptr; + + _ENTER; + + _DEBUG("Header responses:"); + while(NULL != (cursor = ne_response_header_iterate(h->request, cursor, &name, &value))) { + _DEBUG("HEADER: %s: %s", name, value); + if (0 == g_ascii_strncasecmp("accept-ranges", name, 13)) { + /* + * The server advertises range capability. we need "bytes" + */ + if (NULL != g_strrstr(value, "bytes")) { + _DEBUG("server can_ranges"); + h->can_ranges = TRUE; + } + } + + if (0 == g_ascii_strncasecmp("content-length", name, 14)) { + /* + * The server sent us the content length. Parse and store. + */ + len = strtol(value, &endptr, 10); + if ((*value != '\0') && (*endptr == '\0')) { + /* + * Valid data. + */ + _DEBUG("Content length as advertised by server: %d", len); + h->content_length = len; + } + } + } + + _LEAVE; +} + +/* + * ----- + */ + +static int open_request(struct neon_handle* handle, unsigned long startbyte) { + + int ret; + + _ENTER; + + handle->request = ne_request_create(handle->session, "GET", handle->purl->path); + ne_print_request_header(handle->request, "Range", "bytes=%ld-", startbyte); + + /* + * Try to connect to the server. + */ + _DEBUG("Connecting..."); + ret = ne_begin_request(handle->request); + + switch (ret) { + case NE_OK: + /* URL opened OK */ + _DEBUG("URL opened OK"); + handle->content_start = startbyte; + handle->pos = startbyte; + handle_headers(handle); + _LEAVE 0; + break; + + case NE_REDIRECT: + /* We hit a redirect. Handle it. */ + _DEBUG("Redirect encountered"); + handle->redircount += 1; + handle->purl = ne_redirect_location(handle->session); + ne_request_destroy(handle->request); + if (NULL == handle->purl) { + _ERROR("Could not parse redirect response"); + _LEAVE -1; + } + _LEAVE 1; + break; + + default: + /* Something went wrong. */ + _ERROR("Could not open URL: %d", ret); + if (1 == ret) { + _ERROR("neon error string: %s", ne_get_error(handle->session)); + } + ne_request_destroy(handle->request); + _LEAVE -1; + break; + } +} + +/* + * ----- + */ + +static int open_handle(struct neon_handle* handle, unsigned long startbyte) { + + int ret; + + _ENTER; + + handle->redircount = 0; + + _DEBUG("Parsing URL"); + if (0 != ne_uri_parse(handle->url, handle->purl)) { + _ERROR("Could not parse URL '%s'", handle->url); + _LEAVE -1; + } + + if (0 == handle->purl->port) { + handle->purl->port = 80; + } + + while (handle->redircount < 10) { + + _DEBUG("Creating session"); + handle->session = ne_session_create(handle->purl->scheme, handle->purl->host, handle->purl->port); + ne_set_session_flag(handle->session, NE_SESSFLAG_ICYPROTO, 1); + ne_set_session_flag(handle->session, NE_SESSFLAG_PERSIST, 0); + ne_redirect_register(handle->session); + + _DEBUG("Creating request"); + ret = open_request(handle, startbyte); + + if (0 == ret) { + _LEAVE 0; + } else if (-1 == ret) { + ne_session_destroy(handle->session); + _LEAVE -1; + } + } + + /* + * If we get here, our redirect count exceeded + */ + + _ERROR("Redirect count exceeded for URL"); + + _LEAVE 1; +} + +/* + * ----- + */ + +static int fill_buffer(struct neon_handle* h) { + + ssize_t bsize; + char buffer[NETBLKSIZ]; + ssize_t to_read; + + _ENTER; + + bsize = free_rb(&h->rb); + to_read = MIN(bsize, NETBLKSIZ); + + _DEBUG("%d bytes free in buffer, trying to read %d bytes max", bsize, to_read); + + _DEBUG("Reading from the network...."); + if (0 >= (bsize = ne_read_response_block(h->request, buffer, to_read))) { + if (0 == bsize) { + _DEBUG("End of file encountered"); + _LEAVE 1; + } else { + _ERROR("Error while reading from the network"); + _LEAVE -1; + } + } + _DEBUG("Read %d bytes from the network", bsize); + + if (0 != write_rb(&(h->rb), buffer, bsize)) { + _ERROR("Error putting data into buffer"); + _LEAVE -1; + } + + _LEAVE 0; +} + +/* + * ----- + */ + +static int fill_buffer_limit(struct neon_handle* h, unsigned int maxfree) { + + ssize_t bfree; + int ret; + + _ENTER; + + bfree = free_rb(&h->rb); + _DEBUG("Filling buffer up to max %d bytes free, %d bytes free now", maxfree, bfree); + + while (bfree > maxfree) { + ret = fill_buffer(h); + if (-1 == ret) { + _ERROR("Error while filling buffer"); + _LEAVE ret; + } else if (1 == ret) { + /* + * EOF while filling the buffer. Return what we have. + */ + _LEAVE 0; + } + + bfree = free_rb(&h->rb); + } + + _LEAVE 0; +} + +/* + * ----- + */ + +static gpointer reader_thread(void* data) { + + struct neon_handle* h = (struct neon_handle*)data; + int ret; + + _ENTER; + + g_mutex_lock(h->reader_status.mutex); + + while(h->reader_status.reading) { + g_mutex_unlock(h->reader_status.mutex); + + /* + * Hit the network only if we have more than NETBLKSIZ of free buffer + */ + if (NETBLKSIZ < free_rb(&h->rb)) { + + _DEBUG("Filling buffer..."); + ret = fill_buffer(h); + + g_mutex_lock(h->reader_status.mutex); + if (-1 == ret) { + /* + * Error encountered while reading from the network. + * Set the error flag and terminate the + * reader thread. + */ + _DEBUG("Error while reading from the network. Terminating reader thread"); + h->reader_status.status = NEON_READER_ERROR; + g_mutex_unlock(h->reader_status.mutex); + _LEAVE NULL; + } else if (1 == ret) { + /* + * EOF encountered while reading from the + * network. Set the EOF status and exit. + */ + _DEBUG("EOF encountered while reading from the network. Terminating reader thread"); + h->reader_status.status = NEON_READER_EOF; + g_mutex_unlock(h->reader_status.mutex); + _LEAVE NULL; + } + + /* + * So we actually got some data out of the stream. + */ + _DEBUG("Network read succeeded"); + } else { + /* + * Not enough free space in the buffer. + * Sleep until the main thread wakes us up. + */ + g_mutex_lock(h->reader_status.mutex); + if (h->reader_status.reading) { + _DEBUG("Reader thread going to sleep"); + g_cond_wait(h->reader_status.cond, h->reader_status.mutex); + _DEBUG("Reader thread woke up"); + } else { + /* + * Main thread has ordered termination of this thread. + * Leave the loop. + */ + break; + } + } + } + + _DEBUG("Reader thread terminating gracefully"); + h->reader_status.status = NEON_READER_TERM; + g_mutex_unlock(h->reader_status.mutex); + + _LEAVE NULL; +} + +/* + * ----- + */ + +VFSFile* neon_vfs_fopen_impl(const gchar* path, const gchar* mode) { + + VFSFile* file; + struct neon_handle* handle; + + _ENTER; + + _DEBUG("Trying to open '%s' with neon", path); + + if (NULL == (file = malloc(sizeof(VFSFile)))) { + _ERROR("Could not allocate memory for filehandle"); + _LEAVE NULL; + } + + if (NULL == (handle = handle_init())) { + _ERROR("Could not allocate memory for neon handle"); + free(file); + _LEAVE NULL; + } + + if (NULL == (handle->url = strdup(path))) { + _ERROR("Could not copy URL string"); + handle_free(handle); + free(file); + _LEAVE NULL; + } + + if (0 != open_handle(handle, 0)) { + _ERROR("Could not open URL"); + handle_free(handle); + free(file); + _LEAVE NULL; + } + + file->handle = handle; + file->base = &neon_http_const; + + _LEAVE file; +} + +/* + * ---- + */ + +gint neon_vfs_fclose_impl(VFSFile* file) { + + struct neon_handle* h = (struct neon_handle *)file->handle; + + _ENTER; + + if (NULL != h->reader) { + kill_reader(h); + } + + _DEBUG("Destroying request"); + if (NULL != h->request) { + ne_request_destroy(h->request); + } + + _DEBUG("Destroying session"); + ne_session_destroy(h->session); + + handle_free(h); + + _LEAVE 0; +} + +/* + * ----- + */ + +size_t neon_vfs_fread_impl(gpointer ptr_, size_t size, size_t nmemb, VFSFile* file) { + + struct neon_handle* h = (struct neon_handle*)file->handle; + int belem; + int ret; + + _ENTER; + + if (NULL == h->request) { + _ERROR("No request to read from, seek gone wrong?"); + _LEAVE 0; + } + + _DEBUG("Requesting %d elements of %d bytes size each (%d bytes total), to be stored at %p", + nmemb, size, (nmemb*size), ptr_); + + /* + * Look how much data is in the buffer + */ + belem = used_rb(&h->rb) / size; + + if ((NULL != h->reader) && (0 == belem)) { + /* + * There is a reader thread, but the buffer is empty. + * If we are running normally we will have to rebuffer. + * Kill the reader thread and restart. + */ + g_mutex_lock(h->reader_status.mutex); + if (NEON_READER_RUN == h->reader_status.status) { + g_mutex_unlock(h->reader_status.mutex); + _ERROR("Buffer underrun, trying rebuffering"); + kill_reader(h); + } else { + g_mutex_unlock(h->reader_status.mutex); + } + } + + if (NULL == h->reader) { + /* + * There is no reader thread yet. Read the first bytes from + * the network ourselves, and then fire up the reader thread + * to keep the buffer filled up. + */ + _DEBUG("Doing initial buffer fill"); + ret = fill_buffer_limit(h, NBUFSIZ/2); + + if (-1 == ret) { + _ERROR("Error while reading from the network"); + _LEAVE 0; + } else if (1 == ret) { + _ERROR("EOF during initial read"); + _LEAVE 0; + } + + /* + * We have some data in the buffer now. + * Start the reader thread. + */ + h->reader_status.reading = TRUE; + if (NULL == (h->reader = g_thread_create(reader_thread, h, TRUE, NULL))) { + h->reader_status.reading = FALSE; + _ERROR("Error creating reader thread!"); + _LEAVE 0; + } + g_mutex_lock(h->reader_status.mutex); + h->reader_status.status = NEON_READER_RUN; + g_mutex_unlock(h->reader_status.mutex); + } else { + /* + * There already is a reader thread. Look if it is in good + * shape. + */ + g_mutex_lock(h->reader_status.mutex); + _DEBUG("Reader thread status: %d", h->reader_status.status); + switch (h->reader_status.status) { + case NEON_READER_INIT: + case NEON_READER_RUN: + /* + * All is well, nothing to be done. + */ + break; + case NEON_READER_EOF: + /* + * If there still is data in the buffer, carry on. + * If not, terminate the reader thread and return 0. + */ + if (0 == used_rb(&h->rb)) { + _DEBUG("Reached end of stream"); + g_mutex_unlock(h->reader_status.mutex); + kill_reader(h); + h->eof = TRUE; + _LEAVE 0; + } + break; + case NEON_READER_ERROR: + /* Terminate the reader and return 0 */ + g_mutex_unlock(h->reader_status.mutex); + kill_reader(h); + _LEAVE 0; + break; + case NEON_READER_TERM: + /* + * The reader thread terminated gracefully, most + * likely on our own request. + * We should not get here. + */ + _ERROR("Reader thread terminated and fread() called. How did we get here?"); + g_mutex_unlock(h->reader_status.mutex); + kill_reader(h); + _LEAVE 0; + } + g_mutex_unlock(h->reader_status.mutex); + } + + /* + * Deliver data from the buffer + */ + belem = used_rb(&h->rb) / size; + + if (0 == belem) { + /* + * The buffer is empty, we can deliver no data! + */ + _ERROR("Buffer still underrun, fatal."); + _LEAVE 0; + } + + _DEBUG("%d elements of data in the buffer", belem); + read_rb(&h->rb, ptr_, MIN(belem, nmemb)*size); + + /* + * Signal the network thread to continue reading + */ + _DEBUG("Waking up reader thread"); + g_mutex_lock(h->reader_status.mutex); + g_cond_signal(h->reader_status.cond); + g_mutex_unlock(h->reader_status.mutex); + + h->pos += (MIN(belem, nmemb)*size); + + _DEBUG("Returning %d elements", MIN(belem, nmemb)); + + _LEAVE MIN(belem, nmemb); +} + + +/* + * ----- + */ + +size_t neon_vfs_fwrite_impl(gconstpointer ptr, size_t size, size_t nmemb, VFSFile* file) { + + _ENTER; + + _ERROR("NOT IMPLEMENTED"); + + _LEAVE 0; +} + +/* + * ----- + */ + +gint neon_vfs_getc_impl(VFSFile* file) { + + gchar c; + + _ENTER; + + if (1 != neon_vfs_fread_impl(&c, 1, 1, file)) { + _ERROR("Could not getc()!"); + _LEAVE -1; + } + + _LEAVE c; +} + +/* + * ----- + */ + +gint neon_vfs_ungetc_impl(gint c, VFSFile* stream) { + + _ENTER; + + _ERROR("NOT IMPLEMENTED"); + + _LEAVE 0; +} + +/* + * ----- + */ + +void neon_vfs_rewind_impl(VFSFile* file) { + + _ENTER; + + (void)neon_vfs_fseek_impl(file, 0L, SEEK_SET); + + _LEAVE; +} + +/* + * ----- + */ + +glong neon_vfs_ftell_impl(VFSFile* file) { + + struct neon_handle* h = (struct neon_handle *)file->handle; + + _ENTER; + + _DEBUG("Current file position: %d", h->pos); + + _LEAVE h->pos; +} + +/* + * ----- + */ + +gboolean neon_vfs_feof_impl(VFSFile* file) { + + struct neon_handle* h = (struct neon_handle*)file->handle; + + _ENTER; + + _LEAVE h->eof; +} + +/* + * ----- + */ + +gint neon_vfs_truncate_impl(VFSFile* file, glong size) { + + _ENTER; + + _ERROR("NOT IMPLEMENTED"); + + _LEAVE 0; +} + +/* + * ----- + */ + +gint neon_vfs_fseek_impl(VFSFile* file, glong offset, gint whence) { + + struct neon_handle* h = (struct neon_handle*)file->handle; + long newpos; + long content_length; + + _ENTER; + + _DEBUG("Seek requested: offset %ld, whence %d", offset, whence); + /* + * Two things must be satisfied for us to be able to seek: + * - the server must advertise a content-length + * - the server must advertise accept-ranges: bytes + */ + if ((-1 == h->content_length) || !h->can_ranges) { + _DEBUG("Can not seek due to server restrictions"); + _LEAVE -1; + } + + content_length = h->content_length + h->content_start; + + switch (whence) { + case SEEK_SET: + newpos = offset; + break; + case SEEK_CUR: + newpos = h->pos + offset; + break; + case SEEK_END: + newpos = content_length + offset; + break; + default: + _ERROR("Invalid whence specified"); + _LEAVE -1; + } + + _DEBUG("Position to seek to: %ld, current: %ld", newpos, h->pos); + if (0 > newpos) { + _ERROR("Can not seek before start of stream"); + _LEAVE -1; + } + + if (newpos > content_length) { + _ERROR("Can not seek beyond end of stream"); + _LEAVE -1; + } + + if (newpos == h->pos) { + _LEAVE 0; + } + + /* + * To seek to the new position we have to + * - stop the current reader thread, if there is one + * - destroy the current request + * - dump all data currently in the ringbuffer + * - create a new request starting at newpos + */ + if (NULL != h->reader) { + /* + * There may be a thread still running. + */ + kill_reader(h); + } + + ne_request_destroy(h->request); + ne_session_destroy(h->session); + reset_rb(&h->rb); + + if (0 != open_handle(h, newpos)) { + /* + * Something went wrong while creating the new request. + * There is not much we can do now, we'll set the request + * to NULL, so that fread() will error out on the next + * read request + */ + _ERROR("Error while creating new request!"); + h->request = NULL; + _LEAVE -1; + } + + /* + * Things seem to have worked. The next read request will start + * the reader thread again. + */ + + _LEAVE 0; +} + +/* + * ----- + */ + +gchar *neon_vfs_metadata_impl(VFSFile* file, const gchar * field) { + + _ENTER; + + _ERROR("NOT IMPLEMENTED"); + + _LEAVE NULL; +} + +/* + * ----- + */ + +off_t neon_vfs_fsize_impl(VFSFile* file) { + + struct neon_handle* h = (struct neon_handle*)file->handle; + + _ENTER; + + if (-1 == h->content_length) { + _DEBUG("Unknown content length"); + _LEAVE 0; + } + + _LEAVE (h->content_start + h->content_length); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/neon/neon.h Tue Sep 18 09:26:05 2007 -0500 @@ -0,0 +1,63 @@ +#ifndef _NEON_PLUGIN_H +#define _NEON_PLUGIN_H + +#include <glib.h> +#include <audacious/vfs.h> +#include <ne_session.h> +#include <ne_request.h> +#include <ne_uri.h> +#include "rb.h" + + +static void init(void); +static void fini(void); + +VFSFile *neon_vfs_fopen_impl(const gchar* path, const gchar* mode); +gint neon_vfs_fclose_impl(VFSFile* file); +size_t neon_vfs_fread_impl(gpointer ptr_, size_t size, size_t nmemb, VFSFile* file); +size_t neon_vfs_fwrite_impl(gconstpointer ptr, size_t size, size_t nmemb, VFSFile* file); +gint neon_vfs_getc_impl(VFSFile* file); +gint neon_vfs_ungetc_impl(gint c, VFSFile* file); +void neon_vfs_rewind_impl(VFSFile* file); +glong neon_vfs_ftell_impl(VFSFile* file); +gboolean neon_vfs_feof_impl(VFSFile* file); +gint neon_vfs_truncate_impl(VFSFile* file, glong size); +gint neon_vfs_fseek_impl(VFSFile* file, glong offset, gint whence); +gchar *neon_vfs_metadata_impl(VFSFile* file, const gchar * field); +off_t neon_vfs_fsize_impl(VFSFile* file); + +ne_uri purl; + +typedef enum { + NEON_READER_INIT=0, + NEON_READER_RUN=1, + NEON_READER_ERROR, + NEON_READER_EOF, + NEON_READER_TERM +} neon_reader_t; + +struct reader_status { + GMutex* mutex; + GCond* cond; + gboolean reading; + neon_reader_t status; +}; + +struct neon_handle { + gchar* url; /* The URL, as passed to us */ + ne_uri* purl; /* The URL, parsed into a structure */ + struct ringbuf rb; /* Ringbuffer for our data */ + unsigned char redircount; /* Redirect count for the opened URL */ + long pos; /* Current position in the stream (number of last byte delivered to the player) */ + unsigned long content_start; /* Start position in the stream */ + long content_length; /* Total content length, counting from content_start, if known. -1 if unknown */ + gboolean can_ranges; /* TRUE if the webserver advertised accept-range: bytes */ + ne_session* session; + ne_request* request; + GThread* reader; + struct reader_status reader_status; + gboolean eof; +}; + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/neon/rb.c Tue Sep 18 09:26:05 2007 -0500 @@ -0,0 +1,294 @@ +/* + * Ringbuffer implementation + * + * GPL + */ +#include <string.h> +#include "rb.h" +#include "debug.h" + +#ifdef RB_DEBUG +/* + * An internal assertion function to make sure that the + * ringbuffer structure is consistient. + * + * WARNING: This function will call abort() if the ringbuffer + * is found to be inconsistient. + */ +static void _assert_rb(struct ringbuf* rb) { + + unsigned int realused; + + _ENTER; + + _DEBUG("rb->buf=%p, rb->end=%p, rb->wp=%p, rb->rp=%p, rb->free=%u, rb->used=%u, rb->size=%u", + rb->buf, rb->end, rb->wp, rb->rp, rb->free, rb->used, rb->size); + + if (0 == rb->size) { + _ERROR("Buffer size is 0"); + abort(); + } + + if (NULL == rb->buf) { + _ERROR("Buffer start is NULL"); + abort(); + } + + if (rb->used+rb->free != rb->size) { + _ERROR("rb->free and rb->used do not add up to rb->size"); + abort(); + } + + if (rb->buf+(rb->size-1) != rb->end) { + _ERROR("rb->buf and rb->end not rb->size bytes apart"); + abort(); + } + + if ((rb->wp < rb->buf) || (rb->wp > rb->end)) { + _ERROR("Write pointer outside buffer space"); + abort(); + } + + if ((rb->rp < rb->buf) || (rb->rp > rb->end)) { + _ERROR("Read pointer outside buffer space"); + abort(); + } + + if (rb->rp <= rb->wp) { + realused = rb->wp - rb->rp; + } else { + realused = (rb->end - rb->rp) + 1 + (rb->wp-rb->buf); + } + + if (rb->used != realused) { + _ERROR("Usage count is inconsistient (is %d, should be %d)", rb->used, realused); + abort(); + } + + _LEAVE; +} +#endif + +/* + * Reset a ringbuffer structure (i.e. discard + * all data inside of it) + */ +void reset_rb(struct ringbuf* rb) { + + _ENTER; + + pthread_mutex_lock(&rb->lock); + + rb->wp = rb->buf; + rb->rp = rb->buf; + rb->free = rb->size; + rb->used = 0; + rb->end = rb->buf+(rb->size-1); + + pthread_mutex_unlock(&rb->lock); + + _LEAVE; +} + +/* + * Initialize a ringbuffer structure (including + * memory allocation. + * + * Return -1 on error + */ +int init_rb(struct ringbuf* rb, unsigned int size) { + + _ENTER; + + if (0 == size) { + _LEAVE -1; + } + + if (0 != pthread_mutex_init(&rb->lock, NULL)) { + _LEAVE -1; + } + + if (NULL == (rb->buf = malloc(size))) { + _LEAVE -1; + } + rb->size = size; + reset_rb(rb); + + ASSERT_RB(rb); + + _LEAVE 0; +} + +/* + * Write size bytes at buf into the ringbuffer. + * Return -1 on error (not enough space in buffer) + */ +int write_rb(struct ringbuf* rb, void* buf, unsigned int size) { + + int ret = -1; + int endfree; + + _ENTER; + + pthread_mutex_lock(&rb->lock); + + ASSERT_RB(rb); + + if (rb->free < size) { + ret = -1; + goto out; + } + + endfree = (rb->end - rb->wp)+1; + if (endfree < size) { + /* + * There is enough space in the buffer, but not in + * one piece. We need to split the copy into two parts. + */ + memcpy(rb->wp, buf, endfree); + memcpy(rb->buf, buf+endfree, size-endfree); + rb->wp = rb->buf + (size-endfree); + } else if (endfree > size) { + /* + * There is more space than needed at the end + */ + memcpy(rb->wp, buf, size); + rb->wp += size; + } else { + /* + * There is exactly the space needed at the end. + * We need to wrap around the read pointer. + */ + memcpy(rb->wp, buf, size); + rb->wp = rb->buf; + } + + rb->free -= size; + rb->used += size; + + ret = 0; + +out: + ASSERT_RB(rb); + pthread_mutex_unlock(&rb->lock); + + _LEAVE ret; +} + +/* + * Read size byes from buffer into buf. + * Return -1 on error (not enough data in buffer) + */ +int read_rb(struct ringbuf* rb, void* buf, unsigned int size) { + + int ret; + + _ENTER; + + pthread_mutex_lock(&rb->lock); + ret = read_rb_locked(rb, buf, size); + pthread_mutex_unlock(&rb->lock); + + _LEAVE ret; +} + +/* + * Read size bytes from buffer into buf, assuming the buffer lock + * is already held. + * Return -1 on error (not enough data in buffer) + */ +int read_rb_locked(struct ringbuf* rb, void* buf, unsigned int size) { + + int endused; + + _ENTER; + + ASSERT_RB(rb); + + if (rb->used < size) { + /* Not enough bytes in buffer */ + _LEAVE -1; + } + + if (rb->rp < rb->wp) { + /* + Read pointer is behind write pointer, all the data is available in one cunk + */ + memcpy(buf, rb->rp, size); + rb->rp += size; + } else { + /* + * Read pointer is before write pointer + */ + endused = (rb->end - rb->rp)+1; + + if (size < endused) { + /* + * Data is available in one chunk + */ + memcpy(buf, rb->rp, size); + rb->rp += size; + } else { + /* + * There is enough data in the buffer, but it is fragmented. + */ + memcpy(buf, rb->rp, endused); + memcpy(buf+endused, rb->buf, size-endused); + rb->rp = rb->buf + (size-endused); + } + } + + rb->free += size; + rb->used -= size; + + ASSERT_RB(rb); + + _LEAVE 0; +} + +/* + * Return the amount of free space currently in the rb + */ +unsigned int free_rb(struct ringbuf* rb) { + + unsigned int f; + + _ENTER; + + pthread_mutex_lock(&rb->lock); + f = rb->free; + pthread_mutex_unlock(&rb->lock); + + _LEAVE f; +} + + +/* + * Return the amount of used space currently in the rb + */ +unsigned int used_rb(struct ringbuf* rb) { + + unsigned int u; + + _ENTER; + + pthread_mutex_lock(&rb->lock); + u = rb->used; + pthread_mutex_unlock(&rb->lock); + + _LEAVE u; +} + + +/* + * destroy a ringbuffer + */ +void destroy_rb(struct ringbuf* rb) { + + _ENTER; + pthread_mutex_lock(&rb->lock); + free(rb->buf); + pthread_mutex_unlock(&rb->lock); + + _LEAVE; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/neon/rb.h Tue Sep 18 09:26:05 2007 -0500 @@ -0,0 +1,33 @@ +#ifndef _RB_H +#define _RB_H + +#include <pthread.h> +#include <stdlib.h> + +#ifdef RB_DEBUG +#define ASSERT_RB(buf) _assert_rb(buf) +#else +#define ASSERT_RB(buf) +#endif + +struct ringbuf { + pthread_mutex_t lock; + char* buf; + char* end; + char* wp; + char* rp; + unsigned int free; + unsigned int used; + unsigned int size; +}; + +int init_rb(struct ringbuf* rb, unsigned int size); +int write_rb(struct ringbuf* rb, void* buf, unsigned int size); +int read_rb(struct ringbuf* rb, void* buf, unsigned int size); +int read_rb_locked(struct ringbuf* rb, void* buf, unsigned int size); +void reset_rb(struct ringbuf* rb); +unsigned int free_rb(struct ringbuf* rb); +unsigned int used_rb(struct ringbuf* rb); +void destroy_rb(struct ringbuf* rb); + +#endif