Hi, Here's a patch to add support for streaming HTTP transport to the xmms plugin. Most of the code (in particular, http.c) is taken from the xmms mpg123 input plugin. You'll need to make a three-line change to the xmms mpg123 input plugin to get streaming to work. The mpeg plugin currently handles all http urls itself unless they end in an ogg or real audio extension (which means flac streams never make it to the flac plugin). I posted a patch to the xmms devel list back in January (http://lists.xmms.org/pipermail/xmms-devel/2003-January/002493.html), but it was ignored. Maybe you (Josh) could try prodding them. You might get three "const qualifier ignored on asm" warnings for calls to network-host byte order conversions in http.c. According to google, they are caused by "the GLib 1.2 GUINT32_SWAP_LE_BE ASM macros for x86;" I can't get rid of them. There are now two separate decoders used by plugin.c: a file decoder, and a stream decoder. Because the code is C and not C++, I couldn't use polymorphism, so I swap a function table depending on which decoder is in use. If you'd prefer a less drastic change to the code (viz., pervasive if statements), I can try to hack that up. If "#ifdef FLAC_ICECAST"s in configure.c are there to comment out the icecast-related configuration options that existed in the mpg123 config dialog. Icecast currently doesn't support flac (AFAIK), but might one day. The "#if 0"s and commented-out code in http.c are from the original mpg123 code. If your network connection breaks, the clock keeps ticking like it is still playing. This might be considered a bug, but it's the same behavior as in the mpg123 input plugin. ,steven. *** old/Makefile.am Thu Feb 6 21:43:02 2003 --- Makefile.am Thu Jun 5 16:36:54 2003 *************** *** 25,31 **** noinst_HEADERS = \ charset.h \ configure.h \ ! wrap_id3.h CFLAGS = @CFLAGS@ @ID3LIB_CFLAGS@ @XMMS_CFLAGS@ INCLUDES = -I$(top_srcdir)/src --- 25,33 ---- noinst_HEADERS = \ charset.h \ configure.h \ ! wrap_id3.h \ ! http.h \ ! plugin.h CFLAGS = @CFLAGS@ @ID3LIB_CFLAGS@ @XMMS_CFLAGS@ INCLUDES = -I$(top_srcdir)/src *************** *** 40,46 **** xmmsinputplugin_LTLIBRARIES = libxmms-flac.la ! plugin_sources = charset.c configure.c plugin.c wrap_id3.c fileinfo.c if FLaC__HAS_ID3LIB libxmms_flac_la_SOURCES = $(plugin_sources) --- 42,48 ---- xmmsinputplugin_LTLIBRARIES = libxmms-flac.la ! plugin_sources = charset.c configure.c plugin.c wrap_id3.c fileinfo.c http.c if FLaC__HAS_ID3LIB libxmms_flac_la_SOURCES = $(plugin_sources) *** old/Makefile.lite Thu Jan 2 02:03:30 2003 --- Makefile.lite Thu Jun 5 00:01:53 2003 *************** *** 31,37 **** configure.c \ plugin.c \ fileinfo.c \ ! wrap_id3.c include $(topdir)/build/lib.mk --- 31,38 ---- configure.c \ plugin.c \ fileinfo.c \ ! wrap_id3.c \ ! http.c include $(topdir)/build/lib.mk *** old/configure.c Wed Jun 4 19:45:04 2003 --- configure.c Thu Jun 5 16:30:20 2003 *************** *** 50,55 **** --- 50,70 ---- NULL, /* file_char_set */ NULL /* user_char_set */ }, + /* stream */ + { + 100 /* KB */, /* http_buffer_size */ + 50, /* http_prebuffer */ + FALSE, /* use_proxy */ + "", /* proxy_host */ + 0, /* proxy_port */ + FALSE, /* proxy_use_auth */ + "", /* proxy_user */ + "", /* proxy_pass */ + FALSE, /* save_http_stream */ + "", /* save_http_path */ + FALSE, /* cast_title_streaming */ + FALSE /* use_udp_channel */ + }, /* output */ { /* replaygain */ *************** *** 95,100 **** --- 110,127 ---- static GtkWidget *resolution_replaygain_bps_out_radio_16bps; static GtkWidget *resolution_replaygain_bps_out_radio_24bps; + static GtkObject *streaming_size_adj, *streaming_pre_adj; + static GtkWidget *streaming_proxy_use, *streaming_proxy_host_entry; + static GtkWidget *streaming_proxy_port_entry, *streaming_save_use, *streaming_save_entry; + static GtkWidget *streaming_proxy_auth_use; + static GtkWidget *streaming_proxy_auth_pass_entry, *streaming_proxy_auth_user_entry; + static GtkWidget *streaming_proxy_auth_user_label, *streaming_proxy_auth_pass_label; + #ifdef FLAC_ICECAST + static GtkWidget *streaming_cast_title, *streaming_udp_title; + #endif + static GtkWidget *streaming_proxy_hbox, *streaming_proxy_auth_hbox, *streaming_save_dirbrowser; + static GtkWidget *streaming_save_hbox; + static gchar *gtk_entry_get_text_1 (GtkWidget *widget); static void flac_configurewin_ok(GtkWidget * widget, gpointer data); static void configure_destroy(GtkWidget * w, gpointer data); *************** *** 129,134 **** --- 156,216 ---- xmms_cfg_write_boolean(cfg, "flac", "output.resolution.replaygain.dither", flac_cfg.output.resolution.replaygain.dither); xmms_cfg_write_int(cfg, "flac", "output.resolution.replaygain.noise_shaping", flac_cfg.output.resolution.replaygain.noise_shaping); xmms_cfg_write_int(cfg, "flac", "output.resolution.replaygain.bps_out", flac_cfg.output.resolution.replaygain.bps_out); + /* streaming */ + flac_cfg.stream.http_buffer_size = (gint) GTK_ADJUSTMENT(streaming_size_adj)->value; + flac_cfg.stream.http_prebuffer = (gint) GTK_ADJUSTMENT(streaming_pre_adj)->value; + + flac_cfg.stream.use_proxy = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_proxy_use)); + g_free(flac_cfg.stream.proxy_host); + flac_cfg.stream.proxy_host = g_strdup(gtk_entry_get_text(GTK_ENTRY(streaming_proxy_host_entry))); + flac_cfg.stream.proxy_port = atoi(gtk_entry_get_text(GTK_ENTRY(streaming_proxy_port_entry))); + + flac_cfg.stream.proxy_use_auth = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_proxy_auth_use)); + + if(flac_cfg.stream.proxy_user) + g_free(flac_cfg.stream.proxy_user); + flac_cfg.stream.proxy_user = NULL; + if(strlen(gtk_entry_get_text(GTK_ENTRY(streaming_proxy_auth_user_entry))) > 0) + flac_cfg.stream.proxy_user = g_strdup(gtk_entry_get_text(GTK_ENTRY(streaming_proxy_auth_user_entry))); + + if(flac_cfg.stream.proxy_pass) + g_free(flac_cfg.stream.proxy_pass); + flac_cfg.stream.proxy_pass = NULL; + if(strlen(gtk_entry_get_text(GTK_ENTRY(streaming_proxy_auth_pass_entry))) > 0) + flac_cfg.stream.proxy_pass = g_strdup(gtk_entry_get_text(GTK_ENTRY(streaming_proxy_auth_pass_entry))); + + + flac_cfg.stream.save_http_stream = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_save_use)); + if (flac_cfg.stream.save_http_path) + g_free(flac_cfg.stream.save_http_path); + flac_cfg.stream.save_http_path = g_strdup(gtk_entry_get_text(GTK_ENTRY(streaming_save_entry))); + + #ifdef FLAC_ICECAST + flac_cfg.stream.cast_title_streaming = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_cast_title)); + flac_cfg.stream.use_udp_channel = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_udp_title)); + #endif + + xmms_cfg_write_int(cfg, "flac", "stream.http_buffer_size", flac_cfg.stream.http_buffer_size); + xmms_cfg_write_int(cfg, "flac", "stream.http_prebuffer", flac_cfg.stream.http_prebuffer); + xmms_cfg_write_boolean(cfg, "flac", "stream.use_proxy", flac_cfg.stream.use_proxy); + xmms_cfg_write_string(cfg, "flac", "stream.proxy_host", flac_cfg.stream.proxy_host); + xmms_cfg_write_int(cfg, "flac", "stream.proxy_port", flac_cfg.stream.proxy_port); + xmms_cfg_write_boolean(cfg, "flac", "stream.proxy_use_auth", flac_cfg.stream.proxy_use_auth); + if(flac_cfg.stream.proxy_user) + xmms_cfg_write_string(cfg, "flac", "stream.proxy_user", flac_cfg.stream.proxy_user); + else + xmms_cfg_remove_key(cfg, "flac", "stream.proxy_user"); + if(flac_cfg.stream.proxy_pass) + xmms_cfg_write_string(cfg, "flac", "stream.proxy_pass", flac_cfg.stream.proxy_pass); + else + xmms_cfg_remove_key(cfg, "flac", "stream.proxy_pass"); + xmms_cfg_write_boolean(cfg, "flac", "stream.save_http_stream", flac_cfg.stream.save_http_stream); + xmms_cfg_write_string(cfg, "flac", "stream.save_http_path", flac_cfg.stream.save_http_path); + #ifdef FLAC_ICECAST + xmms_cfg_write_boolean(cfg, "flac", "stream.cast_title_streaming", flac_cfg.stream.cast_title_streaming); + xmms_cfg_write_boolean(cfg, "flac", "stream.use_udp_channel", flac_cfg.stream.use_udp_channel); + #endif + xmms_cfg_write_file(cfg, filename); xmms_cfg_free(cfg); g_free(filename); *************** *** 227,232 **** --- 309,370 ---- ; } + static void proxy_use_cb(GtkWidget * w, gpointer data) + { + gboolean use_proxy, use_proxy_auth; + (void) w; + (void) data; + + use_proxy = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_proxy_use)); + use_proxy_auth = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_proxy_auth_use)); + + gtk_widget_set_sensitive(streaming_proxy_hbox, use_proxy); + gtk_widget_set_sensitive(streaming_proxy_auth_use, use_proxy); + gtk_widget_set_sensitive(streaming_proxy_auth_hbox, use_proxy && use_proxy_auth); + } + + static void proxy_auth_use_cb(GtkWidget *w, gpointer data) + { + gboolean use_proxy, use_proxy_auth; + (void) w; + (void) data; + + use_proxy = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_proxy_use)); + use_proxy_auth = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_proxy_auth_use)); + + gtk_widget_set_sensitive(streaming_proxy_auth_hbox, use_proxy && use_proxy_auth); + } + + static void streaming_save_dirbrowser_cb(gchar * dir) + { + gtk_entry_set_text(GTK_ENTRY(streaming_save_entry), dir); + } + + static void streaming_save_browse_cb(GtkWidget * w, gpointer data) + { + (void) w; + (void) data; + if (!streaming_save_dirbrowser) + { + streaming_save_dirbrowser = xmms_create_dir_browser(_("Select the directory where you want to store the MPEG streams:"), + flac_cfg.stream.save_http_path, GTK_SELECTION_SINGLE, streaming_save_dirbrowser_cb); + gtk_signal_connect(GTK_OBJECT(streaming_save_dirbrowser), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &streaming_save_dirbrowser); + gtk_window_set_transient_for(GTK_WINDOW(streaming_save_dirbrowser), GTK_WINDOW(flac_configurewin)); + gtk_widget_show(streaming_save_dirbrowser); + } + } + + static void streaming_save_use_cb(GtkWidget * w, gpointer data) + { + gboolean save_stream; + (void) w; + (void) data; + + save_stream = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_save_use)); + + gtk_widget_set_sensitive(streaming_save_hbox, save_stream); + } + void FLAC_XMMS__configure(void) { *************** *** 239,244 **** --- 377,395 ---- GtkWidget *bbox, *ok, *cancel; GList *list; + GtkWidget *streaming_vbox; + GtkWidget *streaming_buf_frame, *streaming_buf_hbox; + GtkWidget *streaming_size_box, *streaming_size_label, *streaming_size_spin; + GtkWidget *streaming_pre_box, *streaming_pre_label, *streaming_pre_spin; + GtkWidget *streaming_proxy_frame, *streaming_proxy_vbox; + GtkWidget *streaming_proxy_port_label, *streaming_proxy_host_label; + GtkWidget *streaming_save_frame, *streaming_save_vbox; + GtkWidget *streaming_save_label, *streaming_save_browse; + #ifdef FLAC_ICECAST + GtkWidget *streaming_cast_frame, *streaming_cast_vbox; + #endif + char *temp; + if (flac_configurewin != NULL) { gdk_window_raise(flac_configurewin->window); return; *************** *** 462,467 **** --- 613,765 ---- gtk_notebook_append_page(GTK_NOTEBOOK(notebook), output_vbox, gtk_label_new(_("Output"))); + /* Streaming */ + + streaming_vbox = gtk_vbox_new(FALSE, 0); + + streaming_buf_frame = gtk_frame_new(_("Buffering:")); + gtk_container_set_border_width(GTK_CONTAINER(streaming_buf_frame), 5); + gtk_box_pack_start(GTK_BOX(streaming_vbox), streaming_buf_frame, FALSE, FALSE, 0); + + streaming_buf_hbox = gtk_hbox_new(TRUE, 5); + gtk_container_set_border_width(GTK_CONTAINER(streaming_buf_hbox), 5); + gtk_container_add(GTK_CONTAINER(streaming_buf_frame), streaming_buf_hbox); + + streaming_size_box = gtk_hbox_new(FALSE, 5); + /*gtk_table_attach_defaults(GTK_TABLE(streaming_buf_table),streaming_size_box,0,1,0,1); */ + gtk_box_pack_start(GTK_BOX(streaming_buf_hbox), streaming_size_box, TRUE, TRUE, 0); + streaming_size_label = gtk_label_new(_("Buffer size (kb):")); + gtk_box_pack_start(GTK_BOX(streaming_size_box), streaming_size_label, FALSE, FALSE, 0); + streaming_size_adj = gtk_adjustment_new(flac_cfg.stream.http_buffer_size, 4, 4096, 4, 4, 4); + streaming_size_spin = gtk_spin_button_new(GTK_ADJUSTMENT(streaming_size_adj), 8, 0); + gtk_widget_set_usize(streaming_size_spin, 60, -1); + gtk_box_pack_start(GTK_BOX(streaming_size_box), streaming_size_spin, FALSE, FALSE, 0); + + streaming_pre_box = gtk_hbox_new(FALSE, 5); + /*gtk_table_attach_defaults(GTK_TABLE(streaming_buf_table),streaming_pre_box,1,2,0,1); */ + gtk_box_pack_start(GTK_BOX(streaming_buf_hbox), streaming_pre_box, TRUE, TRUE, 0); + streaming_pre_label = gtk_label_new(_("Pre-buffer (percent):")); + gtk_box_pack_start(GTK_BOX(streaming_pre_box), streaming_pre_label, FALSE, FALSE, 0); + streaming_pre_adj = gtk_adjustment_new(flac_cfg.stream.http_prebuffer, 0, 90, 1, 1, 1); + streaming_pre_spin = gtk_spin_button_new(GTK_ADJUSTMENT(streaming_pre_adj), 1, 0); + gtk_widget_set_usize(streaming_pre_spin, 60, -1); + gtk_box_pack_start(GTK_BOX(streaming_pre_box), streaming_pre_spin, FALSE, FALSE, 0); + + /* + * Proxy config. + */ + streaming_proxy_frame = gtk_frame_new(_("Proxy:")); + gtk_container_set_border_width(GTK_CONTAINER(streaming_proxy_frame), 5); + gtk_box_pack_start(GTK_BOX(streaming_vbox), streaming_proxy_frame, FALSE, FALSE, 0); + + streaming_proxy_vbox = gtk_vbox_new(FALSE, 5); + gtk_container_set_border_width(GTK_CONTAINER(streaming_proxy_vbox), 5); + gtk_container_add(GTK_CONTAINER(streaming_proxy_frame), streaming_proxy_vbox); + + streaming_proxy_use = gtk_check_button_new_with_label(_("Use proxy")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(streaming_proxy_use), flac_cfg.stream.use_proxy); + gtk_signal_connect(GTK_OBJECT(streaming_proxy_use), "clicked", GTK_SIGNAL_FUNC(proxy_use_cb), NULL); + gtk_box_pack_start(GTK_BOX(streaming_proxy_vbox), streaming_proxy_use, FALSE, FALSE, 0); + + streaming_proxy_hbox = gtk_hbox_new(FALSE, 5); + gtk_widget_set_sensitive(streaming_proxy_hbox, flac_cfg.stream.use_proxy); + gtk_box_pack_start(GTK_BOX(streaming_proxy_vbox), streaming_proxy_hbox, FALSE, FALSE, 0); + + streaming_proxy_host_label = gtk_label_new(_("Host:")); + gtk_box_pack_start(GTK_BOX(streaming_proxy_hbox), streaming_proxy_host_label, FALSE, FALSE, 0); + + streaming_proxy_host_entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(streaming_proxy_host_entry), flac_cfg.stream.proxy_host); + gtk_box_pack_start(GTK_BOX(streaming_proxy_hbox), streaming_proxy_host_entry, TRUE, TRUE, 0); + + streaming_proxy_port_label = gtk_label_new(_("Port:")); + gtk_box_pack_start(GTK_BOX(streaming_proxy_hbox), streaming_proxy_port_label, FALSE, FALSE, 0); + + streaming_proxy_port_entry = gtk_entry_new(); + gtk_widget_set_usize(streaming_proxy_port_entry, 50, -1); + temp = g_strdup_printf("%d", flac_cfg.stream.proxy_port); + gtk_entry_set_text(GTK_ENTRY(streaming_proxy_port_entry), temp); + g_free(temp); + gtk_box_pack_start(GTK_BOX(streaming_proxy_hbox), streaming_proxy_port_entry, FALSE, FALSE, 0); + + streaming_proxy_auth_use = gtk_check_button_new_with_label(_("Use authentication")); + gtk_widget_set_sensitive(streaming_proxy_auth_use, flac_cfg.stream.use_proxy); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(streaming_proxy_auth_use), flac_cfg.stream.proxy_use_auth); + gtk_signal_connect(GTK_OBJECT(streaming_proxy_auth_use), "clicked", GTK_SIGNAL_FUNC(proxy_auth_use_cb), NULL); + gtk_box_pack_start(GTK_BOX(streaming_proxy_vbox), streaming_proxy_auth_use, FALSE, FALSE, 0); + + streaming_proxy_auth_hbox = gtk_hbox_new(FALSE, 5); + gtk_widget_set_sensitive(streaming_proxy_auth_hbox, flac_cfg.stream.use_proxy && flac_cfg.stream.proxy_use_auth); + gtk_box_pack_start(GTK_BOX(streaming_proxy_vbox), streaming_proxy_auth_hbox, FALSE, FALSE, 0); + + streaming_proxy_auth_user_label = gtk_label_new(_("Username:")); + gtk_box_pack_start(GTK_BOX(streaming_proxy_auth_hbox), streaming_proxy_auth_user_label, FALSE, FALSE, 0); + + streaming_proxy_auth_user_entry = gtk_entry_new(); + if(flac_cfg.stream.proxy_user) + gtk_entry_set_text(GTK_ENTRY(streaming_proxy_auth_user_entry), flac_cfg.stream.proxy_user); + gtk_box_pack_start(GTK_BOX(streaming_proxy_auth_hbox), streaming_proxy_auth_user_entry, TRUE, TRUE, 0); + + streaming_proxy_auth_pass_label = gtk_label_new(_("Password:")); + gtk_box_pack_start(GTK_BOX(streaming_proxy_auth_hbox), streaming_proxy_auth_pass_label, FALSE, FALSE, 0); + + streaming_proxy_auth_pass_entry = gtk_entry_new(); + if(flac_cfg.stream.proxy_pass) + gtk_entry_set_text(GTK_ENTRY(streaming_proxy_auth_pass_entry), flac_cfg.stream.proxy_pass); + gtk_entry_set_visibility(GTK_ENTRY(streaming_proxy_auth_pass_entry), FALSE); + gtk_box_pack_start(GTK_BOX(streaming_proxy_auth_hbox), streaming_proxy_auth_pass_entry, TRUE, TRUE, 0); + + + /* + * Save to disk config. + */ + streaming_save_frame = gtk_frame_new(_("Save stream to disk:")); + gtk_container_set_border_width(GTK_CONTAINER(streaming_save_frame), 5); + gtk_box_pack_start(GTK_BOX(streaming_vbox), streaming_save_frame, FALSE, FALSE, 0); + + streaming_save_vbox = gtk_vbox_new(FALSE, 5); + gtk_container_set_border_width(GTK_CONTAINER(streaming_save_vbox), 5); + gtk_container_add(GTK_CONTAINER(streaming_save_frame), streaming_save_vbox); + + streaming_save_use = gtk_check_button_new_with_label(_("Save stream to disk")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(streaming_save_use), flac_cfg.stream.save_http_stream); + gtk_signal_connect(GTK_OBJECT(streaming_save_use), "clicked", GTK_SIGNAL_FUNC(streaming_save_use_cb), NULL); + gtk_box_pack_start(GTK_BOX(streaming_save_vbox), streaming_save_use, FALSE, FALSE, 0); + + streaming_save_hbox = gtk_hbox_new(FALSE, 5); + gtk_widget_set_sensitive(streaming_save_hbox, flac_cfg.stream.save_http_stream); + gtk_box_pack_start(GTK_BOX(streaming_save_vbox), streaming_save_hbox, FALSE, FALSE, 0); + + streaming_save_label = gtk_label_new(_("Path:")); + gtk_box_pack_start(GTK_BOX(streaming_save_hbox), streaming_save_label, FALSE, FALSE, 0); + + streaming_save_entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(streaming_save_entry), flac_cfg.stream.save_http_path); + gtk_box_pack_start(GTK_BOX(streaming_save_hbox), streaming_save_entry, TRUE, TRUE, 0); + + streaming_save_browse = gtk_button_new_with_label(_("Browse")); + gtk_signal_connect(GTK_OBJECT(streaming_save_browse), "clicked", GTK_SIGNAL_FUNC(streaming_save_browse_cb), NULL); + gtk_box_pack_start(GTK_BOX(streaming_save_hbox), streaming_save_browse, FALSE, FALSE, 0); + + #ifdef FLAC_ICECAST + streaming_cast_frame = gtk_frame_new(_("SHOUT/Icecast:")); + gtk_container_set_border_width(GTK_CONTAINER(streaming_cast_frame), 5); + gtk_box_pack_start(GTK_BOX(streaming_vbox), streaming_cast_frame, FALSE, FALSE, 0); + + streaming_cast_vbox = gtk_vbox_new(5, FALSE); + gtk_container_add(GTK_CONTAINER(streaming_cast_frame), streaming_cast_vbox); + + streaming_cast_title = gtk_check_button_new_with_label(_("Enable SHOUT/Icecast title streaming")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(streaming_cast_title), flac_cfg.stream.cast_title_streaming); + gtk_box_pack_start(GTK_BOX(streaming_cast_vbox), streaming_cast_title, FALSE, FALSE, 0); + + streaming_udp_title = gtk_check_button_new_with_label(_("Enable Icecast Metadata UDP Channel")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(streaming_udp_title), flac_cfg.stream.use_udp_channel); + gtk_box_pack_start(GTK_BOX(streaming_cast_vbox), streaming_udp_title, FALSE, FALSE, 0); + #endif + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), streaming_vbox, gtk_label_new(_("Streaming"))); + /* Buttons */ bbox = gtk_hbutton_box_new(); *** old/configure.h Wed Jun 4 19:32:17 2003 --- configure.h Thu Jun 5 15:25:53 2003 *************** *** 33,38 **** --- 33,53 ---- } title; struct { + gint http_buffer_size; + gint http_prebuffer; + gboolean use_proxy; + gchar *proxy_host; + gint proxy_port; + gboolean proxy_use_auth; + gchar *proxy_user; + gchar *proxy_pass; + gboolean save_http_stream; + gchar *save_http_path; + gboolean cast_title_streaming; + gboolean use_udp_channel; + } stream; + + struct { struct { gboolean enable; gboolean album_mode; *** old/http.c Thu Jun 5 16:39:29 2003 --- http.c Thu Jun 5 16:33:07 2003 *************** *** 0 **** --- 1,886 ---- + /* XMMS - Cross-platform multimedia player + * Copyright (C) 1998-2000 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies + * + * 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. + */ + #include <sys/types.h> + #include <sys/socket.h> + #include <sys/time.h> + #include <netinet/in.h> + #include <arpa/inet.h> + #include <netdb.h> + #include <glib.h> + #include <string.h> + #include <fcntl.h> + #include <unistd.h> + #include <errno.h> + #include <stdio.h> + #include <stdlib.h> + + #include <pthread.h> + + #include <xmms/util.h> + #include <xmms/plugin.h> + + #include "configure.h" + #include "plugin_common/locale_hack.h" + #include "FLAC/format.h" + #include "plugin.h" + + #define min(x,y) ((x)<(y)?(x):(y)) + #define min3(x,y,z) (min(x,y)<(z)?min(x,y):(z)) + #define min4(x,y,z,w) (min3(x,y,z)<(w)?min3(x,y,z):(w)) + + static gchar *icy_name = NULL; + static gint icy_metaint = 0; + + extern InputPlugin flac_ip; + + #undef DEBUG_UDP + + /* Static udp channel functions */ + static int udp_establish_listener (gint *sock); + static int udp_check_for_data(gint sock); + + static char *flac_http_get_title(char *url); + + static gboolean prebuffering, going, eof = FALSE; + static gint sock, rd_index, wr_index, buffer_length, prebuffer_length; + static guint64 buffer_read = 0; + static gchar *buffer; + static guint64 offset; + static pthread_t thread; + static GtkWidget *error_dialog = NULL; + + static FILE *output_file = NULL; + + #define BASE64_LENGTH(len) (4 * (((len) + 2) / 3)) + + /* Encode the string S of length LENGTH to base64 format and place it + to STORE. STORE will be 0-terminated, and must point to a writable + buffer of at least 1+BASE64_LENGTH(length) bytes. */ + static void base64_encode (const gchar *s, gchar *store, gint length) + { + /* Conversion table. */ + static gchar tbl[64] = { + 'A','B','C','D','E','F','G','H', + 'I','J','K','L','M','N','O','P', + 'Q','R','S','T','U','V','W','X', + 'Y','Z','a','b','c','d','e','f', + 'g','h','i','j','k','l','m','n', + 'o','p','q','r','s','t','u','v', + 'w','x','y','z','0','1','2','3', + '4','5','6','7','8','9','+','/' + }; + gint i; + guchar *p = (guchar *)store; + + /* Transform the 3x8 bits to 4x6 bits, as required by base64. */ + for (i = 0; i < length; i += 3) + { + *p++ = tbl[s[0] >> 2]; + *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)]; + *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)]; + *p++ = tbl[s[2] & 0x3f]; + s += 3; + } + /* Pad the result if necessary... */ + if (i == length + 1) + *(p - 1) = '='; + else if (i == length + 2) + *(p - 1) = *(p - 2) = '='; + /* ...and zero-terminate it. */ + *p = '\0'; + } + + /* Create the authentication header contents for the `Basic' scheme. + This is done by encoding the string `USER:PASS' in base64 and + prepending `HEADER: Basic ' to it. */ + static gchar *basic_authentication_encode (const gchar *user, const gchar *passwd, const gchar *header) + { + gchar *t1, *t2, *res; + gint len1 = strlen (user) + 1 + strlen (passwd); + gint len2 = BASE64_LENGTH (len1); + + t1 = g_strdup_printf("%s:%s", user, passwd); + t2 = g_malloc0(len2 + 1); + base64_encode (t1, t2, len1); + res = g_strdup_printf("%s: Basic %s\r\n", header, t2); + g_free(t2); + g_free(t1); + + return res; + } + + static void parse_url(const gchar * url, gchar ** user, gchar ** pass, gchar ** host, int *port, gchar ** filename) + { + gchar *h, *p, *pt, *f, *temp, *ptr; + + temp = g_strdup(url); + ptr = temp; + + if (!strncasecmp("http://", ptr, 7)) + ptr += 7; + h = strchr(ptr, '@'); + f = strchr(ptr, '/'); + if (h != NULL && (!f || h < f)) + { + *h = '\0'; + p = strchr(ptr, ':'); + if (p != NULL && p < h) + { + *p = '\0'; + p++; + *pass = g_strdup(p); + } + else + *pass = NULL; + *user = g_strdup(ptr); + h++; + ptr = h; + } + else + { + *user = NULL; + *pass = NULL; + h = ptr; + } + pt = strchr(ptr, ':'); + if (pt != NULL && (f == NULL || pt < f)) + { + *pt = '\0'; + *port = atoi(pt + 1); + } + else + { + if (f) + *f = '\0'; + *port = 80; + } + *host = g_strdup(h); + + if (f) + *filename = g_strdup(f + 1); + else + *filename = NULL; + g_free(temp); + } + + void flac_http_close(void) + { + going = FALSE; + + pthread_join(thread, NULL); + g_free(icy_name); + icy_name = NULL; + } + + + static gint http_used(void) + { + if (wr_index >= rd_index) + return wr_index - rd_index; + return buffer_length - (rd_index - wr_index); + } + + static gint http_free(void) + { + if (rd_index > wr_index) + return (rd_index - wr_index) - 1; + return (buffer_length - (wr_index - rd_index)) - 1; + } + + static void http_wait_for_data(gint bytes) + { + while ((prebuffering || http_used() < bytes) && !eof && going) + xmms_usleep(10000); + } + + static void show_error_message(gchar *error) + { + if(!error_dialog) + { + GDK_THREADS_ENTER(); + error_dialog = xmms_show_message(_("Error"), error, _("Ok"), FALSE, + NULL, NULL); + gtk_signal_connect(GTK_OBJECT(error_dialog), + "destroy", + GTK_SIGNAL_FUNC(gtk_widget_destroyed), + &error_dialog); + GDK_THREADS_LEAVE(); + } + } + + int flac_http_read(gpointer data, gint length) + { + gint len, cnt, off = 0, meta_len, meta_off = 0, i; + gchar *meta_data, **tags, *temp, *title; + if (length > buffer_length) { + length = buffer_length; + } + + http_wait_for_data(length); + + if (!going) + return 0; + len = min(http_used(), length); + + while (len && http_used()) + { + if ((flac_cfg.stream.cast_title_streaming) && (icy_metaint > 0) && (buffer_read % icy_metaint) == 0 && (buffer_read > 0)) + { + meta_len = *((guchar *) buffer + rd_index) * 16; + rd_index = (rd_index + 1) % buffer_length; + if (meta_len > 0) + { + http_wait_for_data(meta_len); + meta_data = g_malloc0(meta_len); + if (http_used() >= meta_len) + { + while (meta_len) + { + cnt = min(meta_len, buffer_length - rd_index); + memcpy(meta_data + meta_off, buffer + rd_index, cnt); + rd_index = (rd_index + cnt) % buffer_length; + meta_len -= cnt; + meta_off += cnt; + } + tags = g_strsplit(meta_data, "';", 0); + + for (i = 0; tags[i]; i++) + { + if (!strncasecmp(tags[i], "StreamTitle=", 12)) + { + temp = g_strdup(tags[i] + 13); + title = g_strdup_printf("%s (%s)", temp, icy_name); + set_track_info(title, -1); + g_free(title); + g_free(temp); + } + + } + g_strfreev(tags); + + } + g_free(meta_data); + } + if (!http_used()) + http_wait_for_data(length - off); + cnt = min3(len, buffer_length - rd_index, http_used()); + } + else if ((icy_metaint > 0) && (flac_cfg.stream.cast_title_streaming)) + cnt = min4(len, buffer_length - rd_index, http_used(), icy_metaint - (gint) (buffer_read % icy_metaint)); + else + cnt = min3(len, buffer_length - rd_index, http_used()); + if (output_file) + fwrite(buffer + rd_index, 1, cnt, output_file); + + memcpy((gchar *)data + off, buffer + rd_index, cnt); + rd_index = (rd_index + cnt) % buffer_length; + buffer_read += cnt; + len -= cnt; + off += cnt; + } + if (!off) { + fprintf(stderr, "returning zero\n"); + } + return off; + } + + static gboolean http_check_for_data(void) + { + + fd_set set; + struct timeval tv; + gint ret; + + tv.tv_sec = 0; + tv.tv_usec = 20000; + FD_ZERO(&set); + FD_SET(sock, &set); + ret = select(sock + 1, &set, NULL, NULL, &tv); + if (ret > 0) + return TRUE; + return FALSE; + } + + gint flac_http_read_line(gchar * buf, gint size) + { + gint i = 0; + + while (going && i < size - 1) + { + if (http_check_for_data()) + { + if (read(sock, buf + i, 1) <= 0) + return -1; + if (buf[i] == '\n') + break; + if (buf[i] != '\r') + i++; + } + } + if (!going) + return -1; + buf[i] = '\0'; + return i; + } + + /* returns the file descriptor of the socket, or -1 on error */ + static int http_connect (gchar *url_, gboolean head, guint64 offset) + { + gchar line[1024], *user, *pass, *host, *filename, + *status, *url, *temp, *file; + gchar *chost; + gint cnt, error, err_len, port, cport; + gboolean redirect; + int udp_sock = 0; + fd_set set; + struct hostent *hp; + struct sockaddr_in address; + struct timeval tv; + + url = g_strdup (url_); + + do + { + redirect=FALSE; + + g_strstrip(url); + + parse_url(url, &user, &pass, &host, &port, &filename); + + if ((!filename || !*filename) && url[strlen(url) - 1] != '/') + temp = g_strconcat(url, "/", NULL); + else + temp = g_strdup(url); + g_free(url); + url = temp; + + chost = flac_cfg.stream.use_proxy ? flac_cfg.stream.proxy_host : host; + cport = flac_cfg.stream.use_proxy ? flac_cfg.stream.proxy_port : port; + + sock = socket(AF_INET, SOCK_STREAM, 0); + fcntl(sock, F_SETFL, O_NONBLOCK); + address.sin_family = AF_INET; + + status = g_strdup_printf(_("LOOKING UP %s"), chost); + flac_ip.set_info_text(status); + g_free(status); + + if (!(hp = gethostbyname(chost))) + { + status = g_strdup_printf(_("Couldn't look up host %s"), chost); + show_error_message(status); + g_free(status); + + flac_ip.set_info_text(NULL); + eof = TRUE; + } + + if (!eof) + { + memcpy(&address.sin_addr.s_addr, *(hp->h_addr_list), sizeof (address.sin_addr.s_addr)); + address.sin_port = (gint) g_htons(cport); + + status = g_strdup_printf(_("CONNECTING TO %s:%d"), chost, cport); + flac_ip.set_info_text(status); + g_free(status); + if (connect(sock, (struct sockaddr *) &address, sizeof (struct sockaddr_in)) == -1) + { + if (errno != EINPROGRESS) + { + status = g_strdup_printf(_("Couldn't connect to host %s"), chost); + show_error_message(status); + g_free(status); + + flac_ip.set_info_text(NULL); + eof = TRUE; + } + } + while (going) + { + tv.tv_sec = 0; + tv.tv_usec = 10000; + FD_ZERO(&set); + FD_SET(sock, &set); + if (select(sock + 1, NULL, &set, NULL, &tv) > 0) + { + err_len = sizeof (error); + getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &err_len); + if (error) + { + status = g_strdup_printf(_("Couldn't connect to host %s"), + chost); + show_error_message(status); + g_free(status); + + flac_ip.set_info_text(NULL); + eof = TRUE; + + } + break; + } + } + if (!eof) + { + gchar *auth = NULL, *proxy_auth = NULL; + gchar udpspace[30]; + int udp_port; + + if (flac_cfg.stream.use_udp_channel) + { + udp_port = udp_establish_listener (&udp_sock); + if (udp_port > 0) + sprintf (udpspace, "x-audiocast-udpport: %d\r\n", udp_port); + else + udp_sock = 0; + } + + if(user && pass) + auth = basic_authentication_encode(user, pass, "Authorization"); + + if (flac_cfg.stream.use_proxy) + { + file = g_strdup(url); + if(flac_cfg.stream.proxy_use_auth && flac_cfg.stream.proxy_user && flac_cfg.stream.proxy_pass) + { + proxy_auth = basic_authentication_encode(flac_cfg.stream.proxy_user, + flac_cfg.stream.proxy_pass, + "Proxy-Authorization"); + } + } + else + file = g_strconcat("/", filename, NULL); + + temp = g_strdup_printf("GET %s HTTP/1.0\r\n" + "Host: %s\r\n" + "User-Agent: %s/%s\r\n" + "%s%s%s%s", + file, host, "Reference FLAC Player", FLAC__VERSION_STRING, + proxy_auth ? proxy_auth : "", auth ? auth : "", + flac_cfg.stream.cast_title_streaming ? "Icy-MetaData:1\r\n" : "", + flac_cfg.stream.use_udp_channel ? udpspace : ""); + if (offset && !head) { + gchar *temp_dead = temp; + temp = g_strconcat ("%sRange: %ll-\r\n", temp, offset); + fprintf (stderr, "%s", temp); + g_free (temp_dead); + } + + g_free(file); + if(proxy_auth) + g_free(proxy_auth); + if(auth) + g_free(auth); + write(sock, temp, strlen(temp)); + write(sock, "\r\n", 2); + g_free(temp); + flac_ip.set_info_text(_("CONNECTED: WAITING FOR REPLY")); + while (going && !eof) + { + if (http_check_for_data()) + { + if (flac_http_read_line(line, 1024)) + { + status = strchr(line, ' '); + if (status) + { + if (status[1] == '2') + break; + else if(status[1] == '3' && status[2] == '0' && status[3] == '2') + { + while(going) + { + if(http_check_for_data()) + { + if((cnt = flac_http_read_line(line, 1024)) != -1) + { + if(!cnt) + break; + if(!strncmp(line, "Location:", 9)) + { + g_free(url); + url = g_strdup(line+10); + } + } + else + { + eof=TRUE; + flac_ip.set_info_text(NULL); + break; + } + } + } + redirect=TRUE; + break; + } + else + { + status = g_strdup_printf(_("Couldn't connect to host %s\nServer reported: %s"), chost, status); + show_error_message(status); + g_free(status); + break; + } + } + } + else + { + eof = TRUE; + flac_ip.set_info_text(NULL); + } + } + } + + while (going && !redirect) + { + if (http_check_for_data()) + { + if ((cnt = flac_http_read_line(line, 1024)) != -1) + { + if (!cnt) + break; + if (!strncmp(line, "icy-name:", 9)) + icy_name = g_strdup(line + 9); + else if (!strncmp(line, "x-audiocast-name:", 17)) + icy_name = g_strdup(line + 17); + if (!strncmp(line, "icy-metaint:", 12)) + icy_metaint = atoi(line + 12); + if (!strncmp(line, "x-audiocast-udpport:", 20)) { + #ifdef DEBUG_UDP + fprintf (stderr, "Server wants udp messages on port %d\n", atoi (line + 20)); + #endif + /*udp_serverport = atoi (line + 20);*/ + } + + } + else + { + eof = TRUE; + flac_ip.set_info_text(NULL); + break; + } + } + } + } + } + + if(redirect) + { + if (output_file) + { + fclose(output_file); + output_file = NULL; + } + close(sock); + } + + g_free(user); + g_free(pass); + g_free(host); + g_free(filename); + } while(redirect); + + g_free(url); + return eof ? -1 : sock; + } + + static void *http_buffer_loop(void *arg) + { + gchar *status, *url, *temp, *file; + gint cnt, written; + int udp_sock = 0; + + url = (gchar *) arg; + sock = http_connect (url, false, offset); + + if (sock >= 0 && flac_cfg.stream.save_http_stream) { + gchar *output_name; + file = flac_http_get_title(url); + output_name = file; + if (!strncasecmp(output_name, "http://", 7)) + output_name += 7; + temp = strrchr(output_name, '.'); + if (temp && (!strcasecmp(temp, ".fla") || !strcasecmp(temp, ".flac"))) + *temp = '\0'; + + while ((temp = strchr(output_name, '/'))) + *temp = '_'; + output_name = g_strdup_printf("%s/%s.flac", flac_cfg.stream.save_http_path, output_name); + + g_free(file); + + output_file = fopen(output_name, "wb"); + g_free(output_name); + } + + while (going) + { + + if (!http_used() && !flac_ip.output->buffer_playing()) + prebuffering = TRUE; + if (http_free() > 0 && !eof) + { + if (http_check_for_data()) + { + cnt = min(http_free(), buffer_length - wr_index); + if (cnt > 1024) + cnt = 1024; + written = read(sock, buffer + wr_index, cnt); + if (written <= 0) + { + eof = TRUE; + if (prebuffering) + { + prebuffering = FALSE; + + flac_ip.set_info_text(NULL); + } + + } + else + wr_index = (wr_index + written) % buffer_length; + } + + if (prebuffering) + { + if (http_used() > prebuffer_length) + { + prebuffering = FALSE; + flac_ip.set_info_text(NULL); + } + else + { + status = g_strdup_printf(_("PRE-BUFFERING: %dKB/%dKB"), http_used() / 1024, prebuffer_length / 1024); + flac_ip.set_info_text(status); + g_free(status); + } + + } + } + else + xmms_usleep(10000); + + if (flac_cfg.stream.use_udp_channel && udp_sock != 0) + if (udp_check_for_data(udp_sock) < 0) + { + close(udp_sock); + udp_sock = 0; + } + } + if (output_file) + { + fclose(output_file); + output_file = NULL; + } + if (sock >= 0) { + close(sock); + } + if (udp_sock != 0) + close(udp_sock); + + g_free(buffer); + g_free(url); + + pthread_exit(NULL); + } + + int flac_http_open(gchar * _url, guint64 _offset) + { + gchar *url; + + url = g_strdup(_url); + + rd_index = 0; + wr_index = 0; + buffer_length = flac_cfg.stream.http_buffer_size * 1024; + prebuffer_length = (buffer_length * flac_cfg.stream.http_prebuffer) / 100; + buffer_read = 0; + icy_metaint = 0; + prebuffering = TRUE; + going = TRUE; + eof = FALSE; + buffer = g_malloc(buffer_length); + offset = _offset; + + pthread_create(&thread, NULL, http_buffer_loop, url); + + return 0; + } + + char *flac_http_get_title(char *url) + { + if (icy_name) + return g_strdup(icy_name); + if (g_basename(url) && strlen(g_basename(url)) > 0) + return g_strdup(g_basename(url)); + return g_strdup(url); + } + + /* Start UDP Channel specific stuff */ + + /* Find a good local udp port and bind udp_sock to it, return the port */ + static int udp_establish_listener(int *sock) + { + struct sockaddr_in sin; + socklen_t sinlen = sizeof (struct sockaddr_in); + + #ifdef DEBUG_UDP + fprintf (stderr,"Establishing udp listener\n"); + #endif + + if ((*sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + { + g_log(NULL, G_LOG_LEVEL_CRITICAL, + "udp_establish_listener(): unable to create socket"); + return -1; + } + + memset(&sin, 0, sinlen); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = g_htonl(INADDR_ANY); + + if (bind(*sock, (struct sockaddr *)&sin, sinlen) < 0) + { + g_log(NULL, G_LOG_LEVEL_CRITICAL, + "udp_establish_listener(): Failed to bind socket to localhost: %s", strerror(errno)); + close(*sock); + return -1; + } + if (fcntl(*sock, F_SETFL, O_NONBLOCK) < 0) + { + g_log(NULL, G_LOG_LEVEL_CRITICAL, + "udp_establish_listener(): Failed to set flags: %s", strerror(errno)); + close(*sock); + return -1; + } + + memset(&sin, 0, sinlen); + if (getsockname(*sock, (struct sockaddr *)&sin, &sinlen) < 0) + { + g_log(NULL, G_LOG_LEVEL_CRITICAL, + "udp_establish_listener(): Failed to retrieve socket info: %s", strerror(errno)); + close(*sock); + return -1; + } + + #ifdef DEBUG_UDP + fprintf (stderr,"Listening on local %s:%d\n", inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port)); + #endif + + return g_ntohs(sin.sin_port); + } + + static int udp_check_for_data(int sock) + { + char buf[1025], **lines; + char *valptr; + gchar *title; + gint len, i; + struct sockaddr_in from; + socklen_t fromlen; + + fromlen = sizeof(struct sockaddr_in); + + if ((len = recvfrom(sock, buf, 1024, 0, (struct sockaddr *)&from, &fromlen)) < 0) + { + if (errno != EAGAIN) + { + g_log(NULL, G_LOG_LEVEL_CRITICAL, + "udp_read_data(): Error reading from socket: %s", strerror(errno)); + return -1; + } + return 0; + } + buf[len] = '\0'; + #ifdef DEBUG_UDP + fprintf (stderr,"Received: [%s]\n", buf); + #endif + lines = g_strsplit(buf, "\n", 0); + if (!lines) + return 0; + + for (i = 0; lines[i]; i++) + { + while ((lines[i][strlen(lines[i]) - 1] == '\n') || + (lines[i][strlen(lines[i]) - 1] == '\r')) + lines[i][strlen(lines[i]) - 1] = '\0'; + + valptr = strchr(lines[i], ':'); + + if (!valptr) + continue; + else + valptr++; + + g_strstrip(valptr); + if (!strlen(valptr)) + continue; + + if (strstr(lines[i], "x-audiocast-streamtitle") != NULL) + { + title = g_strdup_printf ("%s (%s)", valptr, icy_name); + if (going) + set_track_info(title, -1); + g_free (title); + } + + #if 0 + else if (strstr(lines[i], "x-audiocast-streamlength") != NULL) + { + if (atoi(valptr) != -1) + set_track_info(NULL, atoi(valptr)); + } + #endif + + else if (strstr(lines[i], "x-audiocast-streammsg") != NULL) + { + /* set_track_info(title, -1); */ + /* xmms_show_message(_("Message"), valptr, _("Ok"), */ + /* FALSE, NULL, NULL); */ + g_message("Stream_message: %s", valptr); + } + + #if 0 + /* Use this to direct your webbrowser.. yeah right.. */ + else if (strstr(lines[i], "x-audiocast-streamurl") != NULL) + { + if (lasturl && g_strcmp (valptr, lasturl)) + { + c_message (stderr, "Song URL: %s\n", valptr); + g_free (lasturl); + lasturl = g_strdup (valptr); + } + } + #endif + else if (strstr(lines[i], "x-audiocast-udpseqnr:") != NULL) + { + gchar obuf[60]; + sprintf(obuf, "x-audiocast-ack: %ld \r\n", atol(valptr)); + if (sendto(sock, obuf, strlen(obuf), 0, (struct sockaddr *) &from, fromlen) < 0) + { + g_log(NULL, G_LOG_LEVEL_WARNING, + "udp_check_for_data(): Unable to send ack to server: %s", strerror(errno)); + } + #ifdef DEBUG_UDP + else + fprintf(stderr,"Sent ack: %s", obuf); + fprintf (stderr,"Remote: %s:%d\n", inet_ntoa(from.sin_addr), g_ntohs(from.sin_port)); + #endif + } + } + g_strfreev(lines); + return 0; + } *** old/http.h Thu Jun 5 16:39:29 2003 --- http.h Thu Jun 5 01:03:30 2003 *************** *** 0 **** --- 1,27 ---- + /* libxmms-flac - XMMS FLAC input plugin + * + * 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 __HTTP_H__ + #define __HTTP_H__ + + extern int flac_http_open(gchar * url, guint64 offset); + extern void flac_http_close(void); + extern int flac_http_read(gpointer data, gint length); + + + #endif + *** old/plugin.c Mon May 19 00:27:13 2003 --- plugin.c Thu Jun 5 16:23:02 2003 *************** *** 21,26 **** --- 21,29 ---- #include <string.h> #include <stdio.h> #include <glib.h> + #include <pwd.h> + #include <sys/types.h> + #include <unistd.h> #include <xmms/plugin.h> #include <xmms/util.h> *************** *** 41,46 **** --- 44,50 ---- #include "configure.h" #include "wrap_id3.h" #include "charset.h" + #include "http.h" #ifdef min #undef min *************** *** 68,73 **** --- 72,106 ---- DitherContext dither_context; } file_info_struct; + typedef FLAC__StreamDecoderWriteStatus (*WriteCallback) (const void *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); + typedef void (*MetadataCallback) (const void *decoder, const FLAC__StreamMetadata *metadata, void *client_data); + typedef void (*ErrorCallback) (const void *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + + typedef struct { + FLAC__bool seekable; + void* (*new_decoder) (void); + FLAC__bool (*set_md5_checking) (void *decoder, FLAC__bool value); + FLAC__bool (*set_source) (void *decoder, const char* source); + FLAC__bool (*set_metadata_ignore_all) (void *decoder); + FLAC__bool (*set_metadata_respond) (void *decoder, FLAC__MetadataType type); + FLAC__bool (*set_write_callback) (void *decoder, WriteCallback value); + FLAC__bool (*set_metadata_callback) (void *decoder, MetadataCallback value); + FLAC__bool (*set_error_callback) (void *decoder, ErrorCallback value); + FLAC__bool (*set_client_data) (void *decoder, void *value); + FLAC__bool (*decoder_init) (void *decoder); + void (*safe_decoder_finish) (void *decoder); + void (*safe_decoder_delete) (void *decoder); + FLAC__bool (*process_until_end_of_metadata) (void *decoder); + FLAC__bool (*process_single) (void *decoder); + FLAC__bool (*is_eof) (void *decoder); + } decoder_funcs_t; + + #define NUM_DECODER_TYPES 2 + typedef enum { + DECODER_FILE, + DECODER_HTTP + } decoder_t; + static void FLAC_XMMS__init(); static int FLAC_XMMS__is_our_file(char *filename); static void FLAC_XMMS__play_file(char *filename); *************** *** 79,90 **** static void FLAC_XMMS__get_song_info(char *filename, char **title, int *length); static void *play_loop_(void *arg); ! static FLAC__bool safe_decoder_init_(const char *filename, FLAC__FileDecoder *decoder); ! static void safe_decoder_finish_(FLAC__FileDecoder *decoder); ! static void safe_decoder_delete_(FLAC__FileDecoder *decoder); ! static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); ! static void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); ! static void error_callback_(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); InputPlugin flac_ip { --- 112,126 ---- static void FLAC_XMMS__get_song_info(char *filename, char **title, int *length); static void *play_loop_(void *arg); ! static FLAC__bool safe_decoder_init_(const char *filename, void **decoderp, decoder_funcs_t const ** fnsp); ! static void file_decoder_safe_decoder_finish_(void *decoder); ! static void file_decoder_safe_decoder_delete_(void *decoder); ! static FLAC__StreamDecoderWriteStatus write_callback_(const void *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); ! static void metadata_callback_(const void *decoder, const FLAC__StreamMetadata *metadata, void *client_data); ! static void error_callback_(const void *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); ! ! static void init_decoder_func_tables(); ! static decoder_t source_to_decoder_type (const char *source); InputPlugin flac_ip { *************** *** 119,134 **** static FLAC__byte sample_buffer_[SAMPLE_BUFFER_SIZE]; static unsigned sample_buffer_first_, sample_buffer_last_; ! static FLAC__FileDecoder *decoder_ = 0; static file_info_struct file_info_; static pthread_t decode_thread_; static FLAC__bool audio_error_ = false; #define BITRATE_HIST_SEGMENT_MSEC 500 /* 500ms * 50 = 25s should be enough */ #define BITRATE_HIST_SIZE 50 static unsigned bitrate_history_[BITRATE_HIST_SIZE]; InputPlugin *get_iplugin_info() { --- 155,174 ---- static FLAC__byte sample_buffer_[SAMPLE_BUFFER_SIZE]; static unsigned sample_buffer_first_, sample_buffer_last_; ! static void *decoder_ = 0; static file_info_struct file_info_; static pthread_t decode_thread_; static FLAC__bool audio_error_ = false; + /* A table of sets of decoder functions, indexed by decoder_t */ + static const decoder_funcs_t* DECODER_FUNCS[NUM_DECODER_TYPES]; + #define BITRATE_HIST_SEGMENT_MSEC 500 /* 500ms * 50 = 25s should be enough */ #define BITRATE_HIST_SIZE 50 static unsigned bitrate_history_[BITRATE_HIST_SIZE]; + static decoder_funcs_t const * decoder_func_table_; InputPlugin *get_iplugin_info() { *************** *** 136,141 **** --- 176,206 ---- return &flac_ip; } + void set_track_info(const char* title, int length_in_msec) + { + if (file_info_.is_playing) { + flac_ip.set_info((char*) title, length_in_msec, file_info_.sample_rate * file_info_.channels * file_info_.bits_per_sample, file_info_.sample_rate, file_info_.channels); + } + } + + static gchar* homedir() + { + gchar *result; + char *env_home = getenv("HOME"); + if (env_home) { + result = g_strdup (env_home); + } else { + uid_t uid = getuid(); + struct passwd *pwent; + do { + pwent = getpwent(); + } while (pwent && pwent->pw_uid != uid); + result = pwent ? g_strdup (pwent->pw_dir) : NULL; + endpwent(); + } + return result; + } + void FLAC_XMMS__init() { ConfigFile *cfg; *************** *** 181,187 **** if(!xmms_cfg_read_int(cfg, "flac", "output.resolution.replaygain.bps_out", &flac_cfg.output.resolution.replaygain.bps_out)) flac_cfg.output.resolution.replaygain.bps_out = 16; ! decoder_ = FLAC__file_decoder_new(); xmms_cfg_free(cfg); } --- 246,274 ---- if(!xmms_cfg_read_int(cfg, "flac", "output.resolution.replaygain.bps_out", &flac_cfg.output.resolution.replaygain.bps_out)) flac_cfg.output.resolution.replaygain.bps_out = 16; ! /* stream */ ! ! xmms_cfg_read_int(cfg, "flac", "stream.http_buffer_size", &flac_cfg.stream.http_buffer_size); ! xmms_cfg_read_int(cfg, "flac", "stream.http_prebuffer", &flac_cfg.stream.http_prebuffer); ! xmms_cfg_read_boolean(cfg, "flac", "stream.use_proxy", &flac_cfg.stream.use_proxy); ! xmms_cfg_read_string(cfg, "flac", "stream.proxy_host", &flac_cfg.stream.proxy_host); ! xmms_cfg_read_int(cfg, "flac", "stream.proxy_port", &flac_cfg.stream.proxy_port); ! xmms_cfg_read_boolean(cfg, "flac", "stream.proxy_use_auth", &flac_cfg.stream.proxy_use_auth); ! xmms_cfg_read_string(cfg, "flac", "stream.proxy_user", &flac_cfg.stream.proxy_user); ! xmms_cfg_read_string(cfg, "flac", "stream.proxy_pass", &flac_cfg.stream.proxy_pass); ! xmms_cfg_read_boolean(cfg, "flac", "stream.save_http_stream", &flac_cfg.stream.save_http_stream); ! if (!xmms_cfg_read_string(cfg, "flac", "stream.save_http_path", &flac_cfg.stream.save_http_path) || ! ! *flac_cfg.stream.save_http_path) { ! if (flac_cfg.stream.save_http_path) ! g_free (flac_cfg.stream.save_http_path); ! flac_cfg.stream.save_http_path = homedir(); ! } ! xmms_cfg_read_boolean(cfg, "flac", "stream.cast_title_streaming", &flac_cfg.stream.cast_title_streaming); ! xmms_cfg_read_boolean(cfg, "flac", "stream.use_udp_channel", &flac_cfg.stream.use_udp_channel); ! ! init_decoder_func_tables(); ! decoder_func_table_ = DECODER_FUNCS [DECODER_FILE]; ! decoder_ = decoder_func_table_ -> new_decoder(); xmms_cfg_free(cfg); } *************** *** 209,225 **** file_info_.play_thread_open = false; file_info_.has_replaygain = false; ! if(0 == (f = fopen(filename, "r"))) ! return; ! fclose(f); if(decoder_ == 0) return; ! if(!safe_decoder_init_(filename, decoder_)) return; ! if(file_info_.has_replaygain && flac_cfg.output.replaygain.enable) { if(flac_cfg.output.resolution.replaygain.bps_out == 8) { file_info_.sample_format = FMT_U8; file_info_.sample_format_bytes_per_sample = 1; --- 296,314 ---- file_info_.play_thread_open = false; file_info_.has_replaygain = false; ! if (source_to_decoder_type (filename) == DECODER_FILE) { ! if(0 == (f = fopen(filename, "r"))) ! return; ! fclose(f); ! } if(decoder_ == 0) return; ! if(!safe_decoder_init_(filename, &decoder_, &decoder_func_table_)) return; ! if(file_info_.has_replaygain && flac_cfg.output.replaygain.enable && flac_cfg.output.resolution.replaygain.dither) { if(flac_cfg.output.resolution.replaygain.bps_out == 8) { file_info_.sample_format = FMT_U8; file_info_.sample_format_bytes_per_sample = 1; *************** *** 231,237 **** else { /*@@@ need some error here like wa2: MessageBox(mod_.hMainWindow, "ERROR: plugin can only handle 8/16-bit samples\n", "ERROR: plugin can only handle 8/16-bit samples", 0); */ fprintf(stderr, "libxmms-flac: can't handle %d bit output\n", flac_cfg.output.resolution.replaygain.bps_out); ! safe_decoder_finish_(decoder_); return; } } --- 320,326 ---- else { /*@@@ need some error here like wa2: MessageBox(mod_.hMainWindow, "ERROR: plugin can only handle 8/16-bit samples\n", "ERROR: plugin can only handle 8/16-bit samples", 0); */ fprintf(stderr, "libxmms-flac: can't handle %d bit output\n", flac_cfg.output.resolution.replaygain.bps_out); ! decoder_func_table_ -> safe_decoder_finish(decoder_); return; } } *************** *** 247,253 **** else { /*@@@ need some error here like wa2: MessageBox(mod_.hMainWindow, "ERROR: plugin can only handle 8/16-bit samples\n", "ERROR: plugin can only handle 8/16-bit samples", 0); */ fprintf(stderr, "libxmms-flac: can't handle %d bit output\n", file_info_.bits_per_sample); ! safe_decoder_finish_(decoder_); return; } } --- 336,342 ---- else { /*@@@ need some error here like wa2: MessageBox(mod_.hMainWindow, "ERROR: plugin can only handle 8/16-bit samples\n", "ERROR: plugin can only handle 8/16-bit samples", 0); */ fprintf(stderr, "libxmms-flac: can't handle %d bit output\n", file_info_.bits_per_sample); ! decoder_func_table_ -> safe_decoder_finish(decoder_); return; } } *************** *** 256,262 **** if(flac_ip.output->open_audio(file_info_.sample_format, file_info_.sample_rate, file_info_.channels) == 0) { audio_error_ = true; ! safe_decoder_finish_(decoder_); return; } --- 345,351 ---- if(flac_ip.output->open_audio(file_info_.sample_format, file_info_.sample_rate, file_info_.channels) == 0) { audio_error_ = true; ! decoder_func_table_ -> safe_decoder_finish(decoder_); return; } *************** *** 277,283 **** pthread_join(decode_thread_, NULL); } flac_ip.output->close_audio(); ! safe_decoder_finish_(decoder_); } } --- 366,372 ---- pthread_join(decode_thread_, NULL); } flac_ip.output->close_audio(); ! decoder_func_table_ -> safe_decoder_finish (decoder_); } } *************** *** 288,298 **** void FLAC_XMMS__seek(int time) { ! file_info_.seek_to_in_sec = time; ! file_info_.eof = false; ! while(file_info_.seek_to_in_sec != -1) ! xmms_usleep(10000); } int FLAC_XMMS__get_time() --- 377,389 ---- void FLAC_XMMS__seek(int time) { ! if (decoder_func_table_->seekable) { ! file_info_.seek_to_in_sec = time; ! file_info_.eof = false; ! while(file_info_.seek_to_in_sec != -1) ! xmms_usleep(10000); ! } } int FLAC_XMMS__get_time() *************** *** 307,313 **** void FLAC_XMMS__cleanup() { ! safe_decoder_delete_(decoder_); decoder_ = 0; } --- 398,404 ---- void FLAC_XMMS__cleanup() { ! decoder_func_table_ -> safe_decoder_delete(decoder_); decoder_ = 0; } *************** *** 321,329 **** if(!FLAC__metadata_get_streaminfo(filename, &streaminfo)) { /* @@@ how to report the error? */ if(title) { ! static const char *errtitle = "Invalid FLAC File: "; ! *title = g_malloc(strlen(errtitle) + 1 + strlen(filename) + 1 + 1); ! sprintf(*title, "%s\"%s\"", errtitle, filename); } if(length_in_msec) *length_in_msec = -1; --- 412,424 ---- if(!FLAC__metadata_get_streaminfo(filename, &streaminfo)) { /* @@@ how to report the error? */ if(title) { ! if (source_to_decoder_type (filename) == DECODER_FILE) { ! static const char *errtitle = "Invalid FLAC File: "; ! *title = g_malloc(strlen(errtitle) + 1 + strlen(filename) + 1 + 1); ! sprintf(*title, "%s\"%s\"", errtitle, filename); ! } else { ! *title = NULL; ! } } if(length_in_msec) *length_in_msec = -1; *************** *** 354,364 **** unsigned s; s = sample_buffer_last_ - sample_buffer_first_; ! if(FLAC__file_decoder_get_state(decoder_) == FLAC__FILE_DECODER_END_OF_FILE) { file_info_.eof = true; break; } ! else if(!FLAC__file_decoder_process_single(decoder_)) { /*@@@ this should probably be a dialog */ fprintf(stderr, "libxmms-flac: READ ERROR processing frame\n"); file_info_.eof = true; --- 449,459 ---- unsigned s; s = sample_buffer_last_ - sample_buffer_first_; ! if(decoder_func_table_ -> is_eof(decoder_)) { file_info_.eof = true; break; } ! else if (!decoder_func_table_ -> process_single(decoder_)) { /*@@@ this should probably be a dialog */ fprintf(stderr, "libxmms-flac: READ ERROR processing frame\n"); file_info_.eof = true; *************** *** 366,372 **** } blocksize = sample_buffer_last_ - sample_buffer_first_ - s; decode_position_frame_last = decode_position_frame; ! if(!FLAC__file_decoder_get_decode_position(decoder_, &decode_position_frame)) decode_position_frame = 0; } if(sample_buffer_last_ - sample_buffer_first_ > 0) { --- 461,467 ---- } blocksize = sample_buffer_last_ - sample_buffer_first_ - s; decode_position_frame_last = decode_position_frame; ! if(!decoder_func_table_->seekable || !FLAC__file_decoder_get_decode_position(decoder_, &decode_position_frame)) decode_position_frame = 0; } if(sample_buffer_last_ - sample_buffer_first_ > 0) { *************** *** 405,411 **** } else xmms_usleep(10000); ! if(file_info_.seek_to_in_sec != -1) { const double distance = (double)file_info_.seek_to_in_sec * 1000.0 / (double)file_info_.length_in_msec; unsigned target_sample = (unsigned)(distance * (double)file_info_.total_samples); if(FLAC__file_decoder_seek_absolute(decoder_, (FLAC__uint64)target_sample)) { --- 500,506 ---- } else xmms_usleep(10000); ! if(decoder_func_table_->seekable && file_info_.seek_to_in_sec != -1) { const double distance = (double)file_info_.seek_to_in_sec * 1000.0 / (double)file_info_.length_in_msec; unsigned target_sample = (unsigned)(distance * (double)file_info_.total_samples); if(FLAC__file_decoder_seek_absolute(decoder_, (FLAC__uint64)target_sample)) { *************** *** 428,434 **** } } ! safe_decoder_finish_(decoder_); /* are these two calls necessary? */ flac_ip.output->buffer_free(); --- 523,529 ---- } } ! decoder_func_table_ -> safe_decoder_finish(decoder_); /* are these two calls necessary? */ flac_ip.output->buffer_free(); *************** *** 440,485 **** return 0; /* to silence the compiler warning about not returning a value */ } ! FLAC__bool safe_decoder_init_(const char *filename, FLAC__FileDecoder *decoder) { ! if(decoder == 0) ! return false; ! safe_decoder_finish_(decoder); ! FLAC__file_decoder_set_md5_checking(decoder, false); ! FLAC__file_decoder_set_filename(decoder, filename); ! FLAC__file_decoder_set_metadata_ignore_all(decoder); ! FLAC__file_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO); ! FLAC__file_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT); ! FLAC__file_decoder_set_write_callback(decoder, write_callback_); ! FLAC__file_decoder_set_metadata_callback(decoder, metadata_callback_); ! FLAC__file_decoder_set_error_callback(decoder, error_callback_); ! FLAC__file_decoder_set_client_data(decoder, &file_info_); ! if(FLAC__file_decoder_init(decoder) != FLAC__FILE_DECODER_OK) ! return false; ! if(!FLAC__file_decoder_process_until_end_of_metadata(decoder)) ! return false; return true; } ! void safe_decoder_finish_(FLAC__FileDecoder *decoder) { ! if(decoder && FLAC__file_decoder_get_state(decoder) != FLAC__FILE_DECODER_UNINITIALIZED) ! FLAC__file_decoder_finish(decoder); } ! void safe_decoder_delete_(FLAC__FileDecoder *decoder) { if(decoder) { ! safe_decoder_finish_(decoder); ! FLAC__file_decoder_delete(decoder); } } ! FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) { file_info_struct *file_info = (file_info_struct *)client_data; const unsigned channels = file_info->channels, wide_samples = frame->header.blocksize; --- 535,720 ---- return 0; /* to silence the compiler warning about not returning a value */ } ! ! /*********** File decoder functions */ ! ! static FLAC__bool file_decoder_init (void *decoder) { ! return FLAC__file_decoder_init( (FLAC__FileDecoder*) decoder) == FLAC__FILE_DECODER_OK; ! } ! static void file_decoder_safe_decoder_finish_(void *decoder) ! { ! if(decoder && FLAC__file_decoder_get_state((FLAC__FileDecoder *) decoder) != FLAC__FILE_DECODER_UNINITIALIZED) ! FLAC__file_decoder_finish((FLAC__FileDecoder *) decoder); ! } ! static void file_decoder_safe_decoder_delete_(void *decoder) ! { ! if(decoder) { ! file_decoder_safe_decoder_finish_(decoder); ! FLAC__file_decoder_delete( (FLAC__FileDecoder *) decoder); ! } ! } ! static FLAC__bool file_decoder_is_eof(void *decoder) ! { ! return FLAC__file_decoder_get_state((FLAC__FileDecoder *) decoder) == FLAC__FILE_DECODER_END_OF_FILE; ! } ! ! static const decoder_funcs_t FILE_DECODER_FUNCTIONS = { ! true, ! (void* (*) (void)) FLAC__file_decoder_new, ! (FLAC__bool (*) (void *, FLAC__bool)) FLAC__file_decoder_set_md5_checking, ! (FLAC__bool (*) (void *, const char*)) FLAC__file_decoder_set_filename, ! (FLAC__bool (*) (void *)) FLAC__file_decoder_set_metadata_ignore_all, ! (FLAC__bool (*) (void *, FLAC__MetadataType)) FLAC__file_decoder_set_metadata_respond, ! (FLAC__bool (*) (void *, WriteCallback)) FLAC__file_decoder_set_write_callback, ! (FLAC__bool (*) (void *, MetadataCallback)) FLAC__file_decoder_set_metadata_callback, ! (FLAC__bool (*) (void *, ErrorCallback)) FLAC__file_decoder_set_error_callback, ! (FLAC__bool (*) (void *, void *)) FLAC__file_decoder_set_client_data, ! (FLAC__bool (*) (void *)) file_decoder_init, ! (void (*) (void *)) file_decoder_safe_decoder_finish_, ! (void (*) (void *)) file_decoder_safe_decoder_delete_, ! (FLAC__bool (*) (void *)) FLAC__file_decoder_process_until_end_of_metadata, ! (FLAC__bool (*) (void *)) FLAC__file_decoder_process_single, ! file_decoder_is_eof ! }; ! ! /*********** HTTP decoder functions */ ! ! static gchar *url_; + static FLAC__bool http_decoder_set_md5_checking (void *decoder, FLAC__bool value) + { + (void) value; + // operation unsupported + return FLAC__stream_decoder_get_state ((const FLAC__StreamDecoder *) decoder) =+ FLAC__STREAM_DECODER_UNINITIALIZED; + } + + static FLAC__bool http_decoder_set_url (void *decoder, const char* url) + { + (void) decoder; + url_ = g_strdup (url); return true; } ! static FLAC__StreamDecoderReadStatus http_decoder_read_callback (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data) ! { ! (void) decoder; ! (void) client_data; ! *bytes = flac_http_read (buffer, *bytes); ! return *bytes ? FLAC__STREAM_DECODER_READ_STATUS_CONTINUE : FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; ! } ! ! static FLAC__bool http_decoder_init (void *decoder) ! { ! flac_http_open (url_, 0); ! g_free (url_); ! FLAC__stream_decoder_set_read_callback (decoder, http_decoder_read_callback); ! return FLAC__stream_decoder_init( (FLAC__StreamDecoder*) decoder) == FLAC__STREAM_DECODER_SEARCH_FOR_METADATA; ! } ! ! static void http_decoder_safe_decoder_finish_(void *decoder) { ! if(decoder && FLAC__stream_decoder_get_state((FLAC__StreamDecoder *) decoder) != FLAC__STREAM_DECODER_UNINITIALIZED) { ! FLAC__stream_decoder_finish((FLAC__StreamDecoder *) decoder); ! flac_http_close(); ! } } ! static void http_decoder_safe_decoder_delete_(void *decoder) { if(decoder) { ! http_decoder_safe_decoder_finish_(decoder); ! FLAC__stream_decoder_delete( (FLAC__StreamDecoder *) decoder); ! } ! } ! ! static FLAC__bool http_decoder_is_eof(void *decoder) ! { ! return FLAC__stream_decoder_get_state((FLAC__StreamDecoder *) decoder) == FLAC__STREAM_DECODER_END_OF_STREAM; ! } ! ! static const decoder_funcs_t HTTP_DECODER_FUNCTIONS = { ! false, ! (void* (*) (void)) FLAC__stream_decoder_new, ! http_decoder_set_md5_checking, ! (FLAC__bool (*) (void *, const char*)) http_decoder_set_url, ! (FLAC__bool (*) (void *)) FLAC__stream_decoder_set_metadata_ignore_all, ! (FLAC__bool (*) (void *, FLAC__MetadataType)) FLAC__stream_decoder_set_metadata_respond, ! (FLAC__bool (*) (void *, WriteCallback)) FLAC__stream_decoder_set_write_callback, ! (FLAC__bool (*) (void *, MetadataCallback)) FLAC__stream_decoder_set_metadata_callback, ! (FLAC__bool (*) (void *, ErrorCallback)) FLAC__stream_decoder_set_error_callback, ! (FLAC__bool (*) (void *, void *)) FLAC__stream_decoder_set_client_data, ! (FLAC__bool (*) (void *)) http_decoder_init, ! (void (*) (void *)) http_decoder_safe_decoder_finish_, ! (void (*) (void *)) http_decoder_safe_decoder_delete_, ! (FLAC__bool (*) (void *)) FLAC__stream_decoder_process_until_end_of_metadata, ! (FLAC__bool (*) (void *)) FLAC__stream_decoder_process_single, ! http_decoder_is_eof ! }; ! ! static decoder_funcs_t const *decoder_func_table_; ! ! static void init_decoder_func_tables() ! { ! DECODER_FUNCS [DECODER_FILE] = & FILE_DECODER_FUNCTIONS; ! DECODER_FUNCS [DECODER_HTTP] = & HTTP_DECODER_FUNCTIONS; ! } ! ! static decoder_t source_to_decoder_type (const char *source) ! { ! return strncasecmp(source, "http://", 7) ? DECODER_FILE : DECODER_HTTP; ! } ! ! static void change_decoder_if_needed (decoder_t new_decoder_type, void **decoderp, decoder_funcs_t const ** fntabp) ! { ! const decoder_funcs_t *new_fn_table = DECODER_FUNCS [new_decoder_type]; ! if (*fntabp != new_fn_table) { ! (*fntabp)->safe_decoder_delete(*decoderp); ! *fntabp = new_fn_table; ! *decoderp = new_fn_table -> new_decoder(); ! } ! } ! ! FLAC__bool safe_decoder_init_(const char *filename, void **decoderp, decoder_funcs_t const ** fntabp) ! { ! if(decoderp == 0 || *decoderp == 0) ! return false; ! ! (*fntabp)->safe_decoder_finish(*decoderp); ! ! change_decoder_if_needed(source_to_decoder_type(filename), decoderp, fntabp); ! ! { ! decoder_funcs_t const *fntab = *fntabp; ! void *decoder = *decoderp; ! ! decoder = *decoderp; ! fntab = *fntabp; ! ! fntab -> set_md5_checking(decoder, false); ! fntab -> set_source(decoder, filename); ! fntab -> set_metadata_ignore_all(decoder); ! fntab -> set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO); ! fntab -> set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT); ! fntab -> set_write_callback(decoder, write_callback_); ! fntab -> set_metadata_callback(decoder, metadata_callback_); ! fntab -> set_error_callback(decoder, error_callback_); ! fntab -> set_client_data(decoder, &file_info_); ! if(!fntab -> decoder_init(decoder)) ! return false; ! ! if(!fntab -> process_until_end_of_metadata(decoder)) ! return false; } + + return true; } ! FLAC__StreamDecoderWriteStatus write_callback_(const void *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) { file_info_struct *file_info = (file_info_struct *)client_data; const unsigned channels = file_info->channels, wide_samples = frame->header.blocksize; *************** *** 527,533 **** return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } ! void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) { file_info_struct *file_info = (file_info_struct *)client_data; (void)decoder; --- 762,768 ---- return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } ! void metadata_callback_(const void *decoder, const FLAC__StreamMetadata *metadata, void *client_data) { file_info_struct *file_info = (file_info_struct *)client_data; (void)decoder; *************** *** 537,554 **** file_info->bits_per_sample = metadata->data.stream_info.bits_per_sample; file_info->channels = metadata->data.stream_info.channels; file_info->sample_rate = metadata->data.stream_info.sample_rate; file_info->length_in_msec = file_info->total_samples * 10 / (file_info->sample_rate / 100); } else if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { double gain, peak; if(grabbag__replaygain_load_from_vorbiscomment(metadata, flac_cfg.output.replaygain.album_mode, &gain, &peak)) { ! file_info_.has_replaygain = true; ! file_info_.replay_scale = grabbag__replaygain_compute_scale_factor(peak, gain, (double)flac_cfg.output.replaygain.preamp, /*prevent_clipping=*/!flac_cfg.output.replaygain.hard_limit); } } } ! void error_callback_(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { file_info_struct *file_info = (file_info_struct *)client_data; (void)decoder; --- 772,815 ---- file_info->bits_per_sample = metadata->data.stream_info.bits_per_sample; file_info->channels = metadata->data.stream_info.channels; file_info->sample_rate = metadata->data.stream_info.sample_rate; + + #ifdef FLAC__DO_DITHER + if(file_info->bits_per_sample == 8) { + file_info->sample_format = FMT_S8; + } + else if(file_info->bits_per_sample == 16 || file_info->bits_per_sample == 24) { + file_info->sample_format = FMT_S16_LE; + } + else { + /*@@@ need some error here like wa2: MessageBox(mod_.hMainWindow, "ERROR: plugin can only handle 8/16/24-bit samples\n", "ERROR: plugin can only handle 8/16/24-bit samples", 0); */ + file_info->abort_flag = true; + return; + } + #else + if(file_info->bits_per_sample == 8) { + file_info->sample_format = FMT_S8; + } + else if(file_info->bits_per_sample == 16) { + file_info->sample_format = FMT_S16_LE; + } + else { + /*@@@ need some error here like wa2: MessageBox(mod_.hMainWindow, "ERROR: plugin can only handle 8/16-bit samples\n", "ERROR: plugin can only handle 8/16-bit samples", 0); */ + file_info->abort_flag = true; + return; + } + #endif file_info->length_in_msec = file_info->total_samples * 10 / (file_info->sample_rate / 100); } else if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { double gain, peak; if(grabbag__replaygain_load_from_vorbiscomment(metadata, flac_cfg.output.replaygain.album_mode, &gain, &peak)) { ! file_info->has_replaygain = true; ! file_info->replay_scale = grabbag__replaygain_compute_scale_factor(peak, gain, (double)flac_cfg.output.replaygain.preamp, /*prevent_clipping=*/!flac_cfg.output.replaygain.hard_limit); } } } ! void error_callback_(const void *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { file_info_struct *file_info = (file_info_struct *)client_data; (void)decoder;