diff -ruNw mt-daapd-cvs-20050223/configure.in mt-daapd-cvs-20050223-tri/configure.in
--- mt-daapd-cvs-20050223/configure.in	2005-02-24 07:29:58.000000000 +0200
+++ mt-daapd-cvs-20050223-tri/configure.in	2005-02-24 17:49:13.340108246 +0200
@@ -52,9 +52,14 @@
 #                           LDFLAGS="${LDFLAGS} -logg -lvorbis";
                            CPPFLAGS="${CPPFLAGS} -DOGGVORBIS")
 
+AC_ARG_ENABLE(flac,[  --enable-flac      Enable FLAC support],
+                      use_flac=true;
+                      CPPFLAGS="${CPPFLAGS} -DFLAC")
+
 AM_CONDITIONAL(COND_REND_HOWL, test x$rend_howl = xtrue)
 AM_CONDITIONAL(COND_REND_POSIX, test x$rend_posix = xtrue)
 AM_CONDITIONAL(COND_OGGVORBIS, test x$use_oggvorbis = xtrue)
+AM_CONDITIONAL(COND_FLAC, test x$use_flac = xtrue)
 
 AM_CONDITIONAL(COND_NEED_STRCASESTR,false)
 AM_CONDITIONAL(COND_NEED_STRSEP,false)
@@ -194,6 +199,21 @@
   fi
 fi
 
+echo USE_FLAC is $use_flac
+
+if test x$use_flac = xtrue; then
+  AC_CHECK_HEADERS(FLAC/metadata.h,, [
+   AC_MSG_ERROR([FLAC/metadata.h not found... Must have libFLAC installed for FLAC support])])
+  AC_CHECK_LIB(FLAC,FLAC__metadata_chain_read,echo "Have FLAC",echo "Must have libFLAC for FLAC support";exit)
+
+  if test x"$STATIC_LIBS" != x"no"; then
+    LDFLAGS="${LDFLAGS} ${STATIC_LIBS}/libFLAC.a"
+    echo "Adding static libFLAC"
+  else
+    LDFLAGS="${LDFLAGS} -lFLAC"
+    echo "Adding dynamic libFLAC"
+  fi
+fi
 
 AC_REPLACE_FUNCS(strcasestr)
 AC_REPLACE_FUNCS(strsep)
diff -ruNw mt-daapd-cvs-20050223/contrib/mt-daapd-ssc-script mt-daapd-cvs-20050223-tri/contrib/mt-daapd-ssc-script
--- mt-daapd-cvs-20050223/contrib/mt-daapd-ssc-script	2005-02-21 09:39:52.000000000 +0200
+++ mt-daapd-cvs-20050223-tri/contrib/mt-daapd-ssc-script	2005-02-24 08:34:18.096913909 +0200
@@ -27,8 +27,10 @@
 #
 # This is quite rudimentary.  Obvious TODO list is as follows:
 #
-# - Start streaming immediatey instead of first converting the entire file.
-# - Implement Apple Lossless, somehow, someone, PLEASE!
+# - Speedup the streaming start.
+# - Make the guessing of the wav length reliable.
+# - Implement Apple Lossless, somehow, someone, PLEASE!  It could be done
+#   by integrating QuickTime 6.5 codecs to MPlayer (currently 6.3)
 # - Possibly implement some kind of caching.
 #
 # I'll probably never have time for above things, but now it should
@@ -40,30 +42,53 @@
 
 require 'sys/syscall.ph';
 
+use POSIX ":sys_wait_h";
 use IO::Handle;
 use IO::File;
+use IPC::Open2;
+use IPC::Open3;
 
 $SIG{PIPE} = 'IGNORE';
 
 {
   umask(0077);
-  if ($#ARGV != 1) {
+  if ($#ARGV < 1) {
     usage();
   }
   my ($fn) = $ARGV[0];
   my ($off) = 0 + $ARGV[1];
+  my ($forgelen) = undef;
+  if ($#ARGV > 1) {
+    $forgelen = 0.0 + $ARGV[2];
+    if ($forgelen < 0.01) {
+      $forgelen = 0.0;
+    }
+  } else {
+    $forgelen = 0.0;
+  }
   if ($off < 0) {
     usage();
   }
-  if ($fn =~ m/^..*\.mp3/i) {
-    mp3_proc($fn, $off);
-  } elsif ($fn =~ m/^..*\.wav$/i) {
-    wav_proc($fn, $off);
-  } elsif ($fn =~ m/^..*\.ogg$/i) {
-    ogg_proc($fn, $off);
+  if ($fn =~ m/^..*\.wav/i) {
+    passthru_proc($fn, $off, $forgelen);
+  } elsif ($fn =~ m/^..*\.wave/i) {
+    passthru_proc($fn, $off, $forgelen);
+  } elsif ($fn =~ m/^..*\.flac/i) {
+    flac_proc($fn, $off, $forgelen);
+  } elsif ($fn =~ m/^..*\.fla/i) {
+    flac_proc($fn, $off, $forgelen);
+  } elsif ($fn =~ m/^..*\.mp3/i) {
+    ffmpeg_proc($fn, $off, $forgelen);
+  } elsif ($fn =~ m/^..*\.mpg/i) {
+    ffmpeg_proc($fn, $off, $forgelen);
+  } elsif ($fn =~ m/^..*\.ogg/i) {
+    ffmpeg_proc($fn, $off, $forgelen);
+  } elsif ($fn =~ m/^..*\.avi/i) {
+    ffmpeg_proc($fn, $off, $forgelen);
+  } elsif ($fn =~ m/^..*\.au/i) {
+    ffmpeg_proc($fn, $off, $forgelen);
   } else {
-    printf STDERR "File \"%s\" is in unknown format.\n", $fn;
-    exit -1;
+    mplayer_proc($fn, $off, $forgelen);
   }
   exit;
 }
@@ -84,72 +109,405 @@
 {
   my ($f) = shift;
   my ($off) = shift;
+  my ($forgelen) = shift;
   my ($buf);
   my ($buflen);
-  my ($buftry) = 1024;
+  my ($buftry) = 4096;
+  my ($hdr);
+
+  $hdr = read_wav_hdr($f);
+  if (! defined $hdr) {
+    return undef;
+  }
+
+  my ($chunk_data_length,
+      $format_data_length,
+      $compression_code,
+      $channel_count,
+      $sample_rate,
+      $sample_bit_length,
+      $data_length) = parse_wav_hdr($hdr);
+
+  if (! defined $chunk_data_length) {
+    print STDERR "Can't parse WAV header.\n";
+    return undef;
+  }
 
-  if ($off != 0) {
-    sysseek($f, $off, 1);
+  if (defined $forgelen) {
+    if ($forgelen > 0.00001) {
+      my ($sec) = int($forgelen);
+      my ($msec) = ($forgelen - (0.0 + int($forgelen))) * 1000;
+      my ($bps) = ($sample_rate * $channel_count * int(($sample_bit_length + 7) / 8));
+      my ($len) = int(($sec * $bps) + (($msec * $bps) / 1000));
+      my ($blocklen);
+      my ($residual);
+      if (($sample_rate == 44100) &&
+	  ($channel_count == 2) &&
+	  ($sample_bit_length == 16)) {
+	# It's probably a CD track so let's round it to next cd block limit (2352 bytes).
+	$blocklen = 2352;
+      } else {
+	# Pad it at least to next valid sample border.
+	$blocklen = ($channel_count * int(($sample_bit_length + 7) / 8))
+      }
+      $residual = $len % $blocklen;
+
+      if ($residual != 0) {
+	my ($pad) = $blocklen - $residual;
+	$len += $pad;
+      }
+      $hdr = forge_wav_hdr($hdr, $len);
+    } else {
+      $hdr = forge_continuous_wav_hdr($hdr);
+    }
+  }
+
+  if (($off > 0) && ($off < length($hdr))) {
+    $hdr = substr($hdr, $off, (length($hdr) - $off));
+  } elsif ($off == length($hdr)) {
+    $hdr = undef;
+  } elsif ($off > length($hdr)) {
+    $hdr = undef;
+    $off -= length($hdr);
+    if (! sysseek($f, $off, 1)) {
+      # It's not seekable.  Then just read until desired offset.
+      while ($off > 0) {
+	my ($rl) = $off > $buftry ? $buftry : $off;
+	$buflen = sysread($f, $buf, $rl);
+	if ($buflen < 1) {
+	  return undef;
+	}
+	$off -= $buflen;
+      }
+    }
+  }
+
+  if (defined $hdr) {
+    if (syswrite (STDOUT, $hdr) != length($hdr)) {
+      print STDERR "Write failed.\n";
+      return undef;
+    }
   }
 
   while (1) {
     $buflen = sysread($f, $buf, $buftry);
-    if ($buflen < 1)
-      {
+    if ($buflen < 1) {
 	return 1;
       }
-    if (syswrite (STDOUT, $buf) != $buflen)
-      {
+    if (syswrite (STDOUT, $buf) != $buflen) {
 	print STDERR "Write failed.\n";
 	return undef;
       }
   }
 }
 
-sub mp3_proc
+sub read_wav_hdr
 {
-  my ($fn) = shift;
-  my ($off) = shift;
-  my ($tf) = tmp_file();
-  my ($f) = undef;
+  my ($f) = shift;
+  my ($hdr) = undef;
+  my ($format_data_length);
 
-  unlink($tf);
-  if (system('mpg123', '--quiet',  '--wav', "$tf", "$fn") == 0) {
-    if (open($f, "< $tf")) {
-      wav_loop($f, $off);
-      close($f);
+  if (sysread($f, $hdr, 44) != 44) {
+    print STDERR "Can't read WAV header.\n";
+    return undef;
     }
+
+  return undef
+    if ((substr($hdr,  0, 4) ne "RIFF"));
+
+  return undef
+    if ((substr($hdr,  8, 4) ne "WAVE"));
+
+  return undef
+    if ((substr($hdr, 12, 4) ne "fmt "));
+
+  $format_data_length = (((((((ord(substr($hdr, 19, 1))) * 256) +
+			     (ord(substr($hdr, 18, 1)))) * 256) +
+			   (ord(substr($hdr, 17, 1)))) * 256) +
+			 (ord(substr($hdr, 16, 1))));
+
+  return undef
+    if (($format_data_length < 16) || ($format_data_length > 256));
+
+  if ($format_data_length > 16) {
+    my ($hdr_cont);
+    my ($cont_len) = $format_data_length - 16;
+    if (sysread($f, $hdr_cont, $cont_len) != $cont_len) {
+      print STDERR "Can't read WAV header.\n";
+      return undef;
   }
-  unlink($tf);
+    $hdr = $hdr . $hdr_cont;
+  }
+
+  # FFMpeg creates sometimes WAVs with fmt chunk longer than 16 bytes.
+  # This is weird, since at least iTunes can't play them.  Let's chop
+  # extra bytes away from the fmt chunk and make the WAV header look
+  # like everything else in the world seems to like it.
+  if ($format_data_length > 16) {
+    my ($chunk_data_length,
+	$format_data_length,
+	$compression_code,
+	$channel_count,
+	$sample_rate,
+	$sample_bit_length,
+	$data_length) = parse_wav_hdr($hdr);
+    $chunk_data_length = $chunk_data_length - ($format_data_length - 16);
+    $hdr = (substr($hdr,  0, 4) .
+	    chr($chunk_data_len % 256) .
+	    chr((int($chunk_data_len / 256)) % 256) .
+	    chr((int($chunk_data_len / (256 * 256))) % 256) .
+	    chr((int($chunk_data_len / (256 * 256 * 256))) % 256) .
+	    substr($hdr,  8, 8) .
+	    chr(16) .
+	    chr(0) .
+	    chr(0) .
+	    chr(0) .
+	    substr($hdr,  20, 16) .
+	    substr($hdr, 20 + $format_data_length, length($hdr) - (20 + $format_data_length)));
+  }
+
+  return $hdr;
 }
 
-sub wav_proc
+sub parse_wav_hdr
+{
+  my ($hdr) = shift;
+  if (length($hdr) < 44) {
+    return undef;
+  }
+
+  my ($chunk_data_length);
+  my ($format_data_length);
+  my ($compression_code);
+  my ($channel_count);
+  my ($sample_rate);
+  my ($sample_bit_length);
+  my ($data_length);
+
+  return undef
+    if ((substr($hdr,  0, 4) ne "RIFF"));
+
+  $chunk_data_length = (((((((ord(substr($hdr, 7, 1))) * 256) +
+			    (ord(substr($hdr, 6, 1)))) * 256) +
+			  (ord(substr($hdr, 5, 1)))) * 256) +
+			(ord(substr($hdr, 4, 1))));
+
+  return undef
+    if ((substr($hdr,  8, 4) ne "WAVE"));
+
+  return undef
+    if ((substr($hdr, 12, 4) ne "fmt "));
+
+  $format_data_length = (((((((ord(substr($hdr, 19, 1))) * 256) +
+			     (ord(substr($hdr, 18, 1)))) * 256) +
+			   (ord(substr($hdr, 17, 1)))) * 256) +
+			 (ord(substr($hdr, 16, 1))));
+
+  return undef
+    if ($format_data_length < 16);
+
+  $compression_code = (((ord(substr($hdr, 21, 1))) * 256) +
+		       (ord(substr($hdr, 20, 1))));
+
+  return undef
+    if ($compression_code != 1);
+
+  $channel_count = (((ord(substr($hdr, 23, 1))) * 256) +
+		    (ord(substr($hdr, 22, 1))));
+
+  return undef
+    if ($channel_count < 1);
+
+  $sample_rate = (((((((ord(substr($hdr, 27, 1))) * 256) +
+		      (ord(substr($hdr, 26, 1)))) * 256) +
+		    (ord(substr($hdr, 25, 1)))) * 256) +
+		  (ord(substr($hdr, 24, 1))));
+
+  $sample_bit_length = (((ord(substr($hdr, 35, 1))) * 256) +
+			(ord(substr($hdr, 34, 1))));
+
+  return undef
+    if ((substr($hdr, $format_data_length + 20, 4) ne "data"));
+
+  $data_length = (((((((ord(substr($hdr, $format_data_length + 24 + 3, 1))) * 256) +
+		      (ord(substr($hdr, $format_data_length + 24 + 2, 1)))) * 256) +
+		    (ord(substr($hdr, $format_data_length + 24 + 1, 1)))) * 256) +
+		  (ord(substr($hdr, $format_data_length + 24 + 0, 1))));
+
+  return ($chunk_data_length,
+	  $format_data_length,
+	  $compression_code,
+	  $channel_count,
+	  $sample_rate,
+	  $sample_bit_length,
+	  $data_length);
+}
+
+sub forge_wav_hdr
+{
+  my ($hdr) = shift;
+  my ($data_len) = shift;
+  my ($chunk_data_len) = $data_len + 36;
+
+  return (substr($hdr,  0, 4) .
+	  chr($chunk_data_len % 256) .
+	  chr((int($chunk_data_len / 256)) % 256) .
+	  chr((int($chunk_data_len / (256 * 256))) % 256) .
+	  chr((int($chunk_data_len / (256 * 256 * 256))) % 256) .
+	  substr($hdr,  8, length($hdr) - 8 - 4) .
+	  chr($data_len % 256) .
+	  chr((int($data_len / 256)) % 256) .
+	  chr((int($data_len / (256 * 256))) % 256) .
+	  chr((int($data_len / (256 * 256 * 256))) % 256));
+}
+
+sub forge_continuous_wav_hdr
+{
+  my ($hdr) = shift;
+
+  return (substr($hdr,  0, 4) .
+	  chr(0xff) .
+	  chr(0xff) .
+	  chr(0xff) .
+	  chr(0xff) .
+	  substr($hdr,  8, length($hdr) - 8 - 4) .
+	  chr(0xff - length($hdr) - 8) .
+	  chr(0xff) .
+	  chr(0xff) .
+	  chr(0xff));
+}
+
+sub passthru_proc
 {
   my ($fn) = shift;
   my ($off) = shift;
+  my ($forgelen) = shift;
   my ($tf) = tmp_file();
   my ($f) = undef;
 
   if (! open($f, "< $fn")) {
     return undef;
   }
-  wav_loop($f, $off);
+  wav_loop($f, $off, undef); # Ignore $forgelen
   close($f);
 }
 
-sub ogg_proc
+sub flac_proc
+{
+  my ($fn) = shift;
+  my ($off) = shift;
+  my ($forgelen) = shift;
+  my ($r) = undef;
+  my ($w) = undef;
+  my ($pid);
+
+  $pid = open2($r, $w, 'flac', '--silent', '--decode', '--stdout', "$fn");
+  wav_loop($r, $off, undef); # Ignore $forgelen
+  close($r);
+  close($w);
+  if (waitpid($pid, WNOHANG) <= 0) {
+    kill(SIGKILL, $pid);
+    waitpid($pid, 0);
+  }
+}
+
+sub ffmpeg_proc
 {
   my ($fn) = shift;
   my ($off) = shift;
+  my ($forgelen) = shift;
+  my ($r) = undef;
+  my ($w) = undef;
+  my ($pid);
+
+  $pid = open2($r, $w, 'ffmpeg', '-vn', '-i', "$fn", '-f', 'wav', '-');
+  wav_loop($r, $off, $forgelen);
+  close($r);
+  close($w);
+  if (waitpid($pid, WNOHANG) <= 0) {
+    kill(SIGKILL, $pid);
+    waitpid($pid, 0);
+  }
+}
+
+sub mplayer_proc
+{
+  my ($fn) = shift;
+  my ($off) = shift;
+  my ($forgelen) = shift;
   my ($tf) = tmp_file();
+  my ($w) = undef;
   my ($f) = undef;
-
+  my ($pid);
+  my ($i);
+  my ($started) = undef;
+  my ($waited) = undef;
+  my ($ready) = undef;
+  my ($prebuf) = 2 * 1024 * 1024;
   unlink($tf);
-  if (system('oggdec', '--quiet', '-o', "$tf", "$fn") == 0) {
+  $pid = open3('>&STDERR', $w, '>&STDERR',
+	       'mplayer',
+	       '-quiet', '-really-quiet',
+	       '-vo', 'null',
+	       '-nomouseinput', '-nojoystick', '-nolirc',
+	       '-noconsolecontrols', '-nortc',
+	       '-aofile', "$tf", '-ao', 'pcm', "$fn");
+
+  # Wait just to see, if the decoding starts.
+  # If the resulting wav is less than 512 bytes,
+  # This will fail, but who cares.
+  foreach $i (0.05, 0.1, 0.2, 0.4, 0.8, 1.6, 3.2) {
+    my ($s);
+    my ($wp);
+    select(undef, undef, undef, $i);
+    $wp = waitpid($pid, WNOHANG);
+    ($_, $_, $_, $_, $_, $_, $_, $s, $_, $_, $_, $_, $_) = stat("$tf");
+    if (defined $s && ($s > 512)) {
+      $started = 1;
+    }
+    if ($wp > 0) {
+      $waited = 1;
+    }
+    if ($started || $waited) {
+      last;
+    }
+  }
+  if (! $started) {
+    kill(SIGKILL, $pid);
+    unlink($tf);
+  }
+
+  # Loop here until we got data beyond the offset point
+  # or the process terminates.
+  my ($sleep) = 0.025;
+  while (! $waited) {
+    my ($s);
+    my ($wp);
+    if ($sleep < 0.5) {
+      $sleep *= 2.0;
+    }
+    select(undef, undef, undef, $sleep);
+    $wp = waitpid($pid, WNOHANG);
+    ($_, $_, $_, $_, $_, $_, $_, $s, $_, $_, $_, $_, $_) = stat("$tf");
+    if (defined $s && ($s > ($off + $prebuf))) {
+      $ready = 1;
+    }
+    if ($wp > 0) {
+      $waited = 1;
+    }
+    if ($ready || $waited) {
+      last;
+    }
+  }
+
+  # Stream.
     if (open($f, "< $tf")) {
-      wav_loop($f, $off);
+    wav_loop($f, $off, $forgelen);
       close($f);
     }
+  if (! $waited) {
+    waitpid($pid, 0);
   }
+  close($w);
   unlink($tf);
 }
diff -ruNw mt-daapd-cvs-20050223/contrib/mt-daapd.conf mt-daapd-cvs-20050223-tri/contrib/mt-daapd.conf
--- mt-daapd-cvs-20050223/contrib/mt-daapd.conf	2005-02-21 10:10:32.000000000 +0200
+++ mt-daapd-cvs-20050223-tri/contrib/mt-daapd.conf	2005-02-24 20:41:35.190349114 +0200
@@ -141,13 +141,15 @@
 #
 # Program that is used in server side format conversion.
 # Program must accept following command line syntax:
-#     ssc_prog filename offset
+#     ssc_prog filename offset length ...
 # Parameter filename is the real name of the file that is
 # to be converted and streamed, offset is number of bytes
 # that are skipped from the beginning of the _output_ file
-# before streaming is started.  The resulting wav file (or
-# rest of the file after initial seek) is written to the
-# standard output by the ssc_prog program.  This is typically
+# before streaming is started, length is length of the song
+# in seconds (or zero).  All other possible arguments must
+# be ignored.  The resulting wav file (or the rest of
+# the file after initial seek) is written to the standard
+# output by the ssc_prog program.  This is typically
 # a script that is a front end for different conversion tools
 # handling different formats.
 #
diff -ruNw mt-daapd-cvs-20050223/src/Makefile.am mt-daapd-cvs-20050223-tri/src/Makefile.am
--- mt-daapd-cvs-20050223/src/Makefile.am	2005-02-21 10:10:33.000000000 +0200
+++ mt-daapd-cvs-20050223-tri/src/Makefile.am	2005-02-24 17:55:06.000440018 +0200
@@ -22,6 +22,10 @@
 OGGVORBISSRC=ogg.c
 endif
 
+if COND_FLAC
+FLACSRC=flac.c
+endif
+
 mt_daapd_SOURCES = main.c daapd.h rend.h uici.c uici.h webserver.c \
 	webserver.h configfile.c configfile.h err.c err.h restart.c restart.h \
 	daap-proto.c daap-proto.h daap.c daap.h db-gdbm.c db-memory.h \
@@ -29,10 +33,10 @@
 	rend-unix.h lexer.l parser.y strcasestr.c strcasestr.h strsep.c \
 	redblack.c redblack.h dynamic-art.c dynamic-art.h query.c query.h \
 	xml-rpc.h xml-rpc.c ssc.c ssc.h \
-	$(PRENDSRC) $(ORENDSRC) $(HRENDSRC) $(OGGVORBISSRC)
+	$(PRENDSRC) $(ORENDSRC) $(HRENDSRC) $(OGGVORBISSRC) $(FLACSRC)
 
 EXTRA_DIST = mDNS.c mDNSClientAPI.h mDNSDebug.h mDNSPosix.c \
 	mDNSUNP.c mDNSPlatformFunctions.h mDNSPosix.h mDNSUNP.h \
 	rend-howl.c rend-posix.c rend-osx.c db-memory.c \
 	db-gdbm.c strcasestr.h redblack.c redblack.h db-memory.c \
-	parser.h ogg.c
+	parser.h ogg.c flac.c
diff -ruNw mt-daapd-cvs-20050223/src/configfile.c mt-daapd-cvs-20050223-tri/src/configfile.c
--- mt-daapd-cvs-20050223/src/configfile.c	2005-02-24 07:04:04.000000000 +0200
+++ mt-daapd-cvs-20050223-tri/src/configfile.c	2005-02-24 17:53:16.574092348 +0200
@@ -1263,6 +1263,10 @@
     ws_writefd(pwsc,"%s ","--enable-oggvorbis");
 #endif
 
+#ifdef FLAC
+    ws_writefd(pwsc,"%s ","--enable-flac");
+#endif
+
 #ifdef WITH_GDBM
     ws_writefd(pwsc,"%s ","--with-gdbm");
 #endif
diff -ruNw mt-daapd-cvs-20050223/src/flac.c mt-daapd-cvs-20050223-tri/src/flac.c
--- mt-daapd-cvs-20050223/src/flac.c	1970-01-01 02:00:00.000000000 +0200
+++ mt-daapd-cvs-20050223-tri/src/flac.c	2005-02-24 19:49:24.541075537 +0200
@@ -0,0 +1,217 @@
+/*
+ * $Id: ssc.c,v 1.1 2005/02/21 08:10:36 rpedde Exp $
+ * Implementation file for server side format conversion.
+ *
+ * Copyright (C) 2005 Timo J. Rinne (tri@iki.fi)
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#define _POSIX_PTHREAD_SEMANTICS
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <id3tag.h>
+#include <limits.h>
+#include <restart.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <netinet/in.h>  /* htons and friends */
+#include <sys/stat.h>
+#include <dirent.h>      /* why here?  For osx 10.2, of course! */
+
+#include "daapd.h"
+#include "db-memory.h"
+#include "err.h"
+#include "mp3-scanner.h"
+
+#ifndef HAVE_STRCASESTR
+# include "strcasestr.h"
+#endif
+
+#include <FLAC/metadata.h>
+
+int scan_get_flacfileinfo(char *filename, MP3FILE *pmp3)
+{
+    FLAC__bool ok;
+    FLAC__Metadata_Chain *chain;
+    FLAC__Metadata_Iterator *iterator;
+    FLAC__StreamMetadata *block;
+    int rv = -1;
+    unsigned int sec, ms;
+    FILE *f;
+
+    if (!(f = fopen(filename, "rb"))) {
+	DPRINTF(E_WARN,L_SCAN,"Could not open %s for reading\n", filename);
+	return -1;
+    }
+    fseek(f, 0, SEEK_END);
+    pmp3->file_size = ftell(f);
+    fseek(f, 0, SEEK_SET);
+    fclose(f);
+
+    chain = FLAC__metadata_chain_new();
+    if (! chain) {
+	DPRINTF(E_WARN,L_SCAN,"Cannot allocate FLAC metadata chain\n");
+        return rv;
+    }
+    if (! FLAC__metadata_chain_read(chain, filename)) {
+	DPRINTF(E_WARN,L_SCAN,"Cannot read FLAC metadata from %s\n", filename);
+	FLAC__metadata_chain_delete(chain);
+	return rv;
+    }
+
+    iterator = FLAC__metadata_iterator_new();
+    if (! iterator) {
+	DPRINTF(E_WARN,L_SCAN,"Cannot allocate FLAC metadata iterator\n");
+	FLAC__metadata_chain_delete(chain);
+	return rv;
+    }
+
+    FLAC__metadata_iterator_init(iterator, chain);
+    do {
+	block = FLAC__metadata_iterator_get_block(iterator);
+	if (block->type == FLAC__METADATA_TYPE_STREAMINFO) {
+	    sec = (unsigned int)(block->data.stream_info.total_samples /
+				 block->data.stream_info.sample_rate);
+	    ms = (unsigned int)(((block->data.stream_info.total_samples %
+				  block->data.stream_info.sample_rate) * 1000) /
+				block->data.stream_info.sample_rate);
+	    if ((sec == 0) && (ms == 0))
+		break; /* Info is crap, escape div-by-zero. */
+	    pmp3->song_length = (sec * 1000) + ms;
+	    pmp3->bitrate = (pmp3->file_size) / (((sec * 1000) + ms) / 8);
+	    pmp3->samplerate = block->data.stream_info.sample_rate;
+	    rv = 0;
+	    break;
+	}
+    } while (FLAC__metadata_iterator_next(iterator));
+
+    if (rv < 0) {
+	DPRINTF(E_WARN,L_SCAN,"Cannot find FLAC metadata in %s\n", filename);
+    }
+
+    FLAC__metadata_iterator_delete(iterator);
+    FLAC__metadata_chain_delete(chain);
+    return rv;
+}
+
+#define GET_VORBIS_COMMENT(comment, name, len)				\
+        (((strncmp(name, (comment).entry, strlen(name)) == 0) &&	\
+	  ((comment).entry[strlen(name)] == '=')) ?			\
+	 ((*(len) = (comment).length - (strlen(name) + 1)),		\
+	  (&((comment).entry[strlen(name) + 1]))) :			\
+	 NULL)
+
+int scan_get_flactags(char *filename, MP3FILE *pmp3)
+{
+    FLAC__bool ok;
+    FLAC__Metadata_Chain *chain;
+    FLAC__Metadata_Iterator *iterator;
+    FLAC__StreamMetadata *block;
+    int rv = -1;
+    int i;
+    char *val;
+    size_t len;
+    char tmp;
+
+    chain = FLAC__metadata_chain_new();
+    if (! chain) {
+	DPRINTF(E_WARN,L_SCAN,"Cannot allocate FLAC metadata chain\n");
+        return rv;
+    }
+    if (! FLAC__metadata_chain_read(chain, filename)) {
+	DPRINTF(E_WARN,L_SCAN,"Cannot read FLAC metadata from %s\n", filename);
+	FLAC__metadata_chain_delete(chain);
+	return rv;
+    }
+
+    iterator = FLAC__metadata_iterator_new();
+    if (! iterator) {
+	DPRINTF(E_WARN,L_SCAN,"Cannot allocate FLAC metadata iterator\n");
+	FLAC__metadata_chain_delete(chain);
+	return rv;
+    }
+
+    FLAC__metadata_iterator_init(iterator, chain);
+    do {
+	block = FLAC__metadata_iterator_get_block(iterator);
+	if (block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
+	    {
+		for (i = 0; i < block->data.vorbis_comment.num_comments; i++) {
+		    if (val = GET_VORBIS_COMMENT(block->data.vorbis_comment.comments[i],
+						 "ARTIST", &len)) {
+			if ((pmp3->artist = calloc(len + 1, 1)) != NULL)
+			    strncpy(pmp3->artist, val, len);
+		    } else if (val = GET_VORBIS_COMMENT(block->data.vorbis_comment.comments[i],
+							"TITLE", &len)) {
+			if ((pmp3->title = calloc(len + 1, 1)) != NULL)
+			    strncpy(pmp3->title, val, len);
+		    } else if (val = GET_VORBIS_COMMENT(block->data.vorbis_comment.comments[i],
+							"ALBUM", &len)) {
+			if ((pmp3->album = calloc(len + 1, 1)) != NULL)
+			    strncpy(pmp3->album, val, len);
+		    } else if (val = GET_VORBIS_COMMENT(block->data.vorbis_comment.comments[i],
+							"GENRE", &len)) {
+			if ((pmp3->genre = calloc(len + 1, 1)) != NULL)
+			    strncpy(pmp3->genre, val, len);
+		    } else if (val = GET_VORBIS_COMMENT(block->data.vorbis_comment.comments[i],
+							"COMPOSER", &len)) {
+			if ((pmp3->composer = calloc(len + 1, 1)) != NULL)
+			    strncpy(pmp3->composer, val, len);
+		    } else if (val = GET_VORBIS_COMMENT(block->data.vorbis_comment.comments[i],
+							"COMMENT", &len)) {
+			if ((pmp3->comment = calloc(len + 1, 1)) != NULL)
+			    strncpy(pmp3->comment, val, len);
+		    } else if (val = GET_VORBIS_COMMENT(block->data.vorbis_comment.comments[i],
+							"TRACKNUMBER", &len)) {
+			tmp = *(val + len);
+			*(val + len) = '\0';
+			pmp3->track = atoi(val);
+			*(val + len) = tmp;
+		    } else if (val = GET_VORBIS_COMMENT(block->data.vorbis_comment.comments[i],
+							"DISCNUMBER", &len)) {
+			tmp = *(val + len);
+			*(val + len) = '\0';
+			pmp3->track = atoi(val);
+			*(val + len) = tmp;
+		    } else if (val = GET_VORBIS_COMMENT(block->data.vorbis_comment.comments[i],
+							"YEAR", &len)) {
+			tmp = *(val + len);
+			*(val + len) = '\0';
+			pmp3->year = atoi(val);
+			*(val + len) = tmp;
+		    }
+		}
+		rv = 0;
+		break;
+	    }
+	}
+    } while (FLAC__metadata_iterator_next(iterator));
+
+    if (rv < 0) {
+	DPRINTF(E_WARN,L_SCAN,"Cannot find FLAC metadata in %s\n", filename);
+    }
+
+    FLAC__metadata_iterator_delete(iterator);
+    FLAC__metadata_chain_delete(chain);
+    return rv;
+}
diff -ruNw mt-daapd-cvs-20050223/src/main.c mt-daapd-cvs-20050223-tri/src/main.c
--- mt-daapd-cvs-20050223/src/main.c	2005-02-21 10:10:35.000000000 +0200
+++ mt-daapd-cvs-20050223-tri/src/main.c	2005-02-24 08:32:54.884234205 +0200
@@ -392,7 +392,9 @@
 	    // The file should be converted in the server side.
 	    DPRINTF(E_WARN,L_WS,"Thread %d: Autoconvert file %s for client\n",
 		    pwsc->threadno,real_path);
-	    file_ptr=server_side_convert_open(real_path,offset);
+	    file_ptr = server_side_convert_open(real_path,
+						offset,
+						pmp3->song_length);
 	    if (file_ptr) {
 		file_fd = fileno(file_ptr);
 	    } else {
diff -ruNw mt-daapd-cvs-20050223/src/mp3-scanner.c mt-daapd-cvs-20050223-tri/src/mp3-scanner.c
--- mt-daapd-cvs-20050223/src/mp3-scanner.c	2005-02-24 07:04:05.000000000 +0200
+++ mt-daapd-cvs-20050223-tri/src/mp3-scanner.c	2005-02-24 18:30:47.019771185 +0200
@@ -281,6 +281,7 @@
 static int scan_get_fileinfo(char *file, MP3FILE *pmp3);
 static int scan_get_mp3fileinfo(char *file, MP3FILE *pmp3);
 static int scan_get_aacfileinfo(char *file, MP3FILE *pmp3);
+static int scan_get_wavfileinfo(char *file, MP3FILE *pmp3);
 static int scan_get_nulfileinfo(char *file, MP3FILE *pmp3) { return 0; };
 static int scan_get_urlfileinfo(char *file, MP3FILE *pmp3);
 
@@ -293,6 +294,12 @@
 
 #ifdef OGGVORBIS
 extern int scan_get_oggfileinfo(char *filename, MP3FILE *pmp3);
+extern int scan_get_oggtags(char *filename, MP3FILE *pmp3);
+#endif
+
+#ifdef FLAC
+extern int scan_get_flacfileinfo(char *filename, MP3FILE *pmp3);
+extern int scan_get_flactags(char *filename, MP3FILE *pmp3);
 #endif
 
 /* 
@@ -311,9 +318,14 @@
     { "m4a", scan_get_aactags, scan_get_aacfileinfo },
     { "m4p", scan_get_aactags, scan_get_aacfileinfo },
     { "mp3", scan_get_mp3tags, scan_get_mp3fileinfo },
+    { "wav", scan_get_nultags, scan_get_wavfileinfo },
     { "url", scan_get_nultags, scan_get_urlfileinfo },
 #ifdef OGGVORBIS
-    { "ogg", scan_get_nultags, scan_get_oggfileinfo },
+    { "ogg", scan_get_oggtags, scan_get_oggfileinfo },
+#endif
+#ifdef FLAC
+    { "flac", scan_get_flactags, scan_get_flacfileinfo },
+    { "fla", scan_get_flactags, scan_get_flacfileinfo },
 #endif
     { NULL, 0 }
 };
@@ -1231,6 +1243,84 @@
     return 0;
 }
 
+#define GET_WAV_INT32(p) ((((unsigned long)((p)[3])) << 24) |	\
+		          (((unsigned long)((p)[2])) << 16) |	\
+		          (((unsigned long)((p)[1])) << 8) |	\
+		          (((unsigned long)((p)[0]))))
+
+#define GET_WAV_INT16(p) ((((unsigned long)((p)[1])) << 8) |	\
+		          (((unsigned long)((p)[0]))))
+
+/*
+ * scan_get_wavfileinfo
+ *
+ * Get info from the actual wav headers
+ */
+int scan_get_wavfileinfo(char *file, MP3FILE *pmp3) {
+    FILE *infile;
+    size_t rl;
+    unsigned char hdr[44];
+    unsigned long chunk_data_length;
+    unsigned long format_data_length;
+    unsigned long compression_code;
+    unsigned long channel_count;
+    unsigned long sample_rate;
+    unsigned long sample_bit_length;
+    unsigned long bit_rate;
+    unsigned long data_length;
+    unsigned long sec, ms;
+
+    DPRINTF(E_DBG,L_SCAN,"Getting WAV file info\n");
+
+    if(!(infile=fopen(file,"rb"))) {
+	DPRINTF(E_WARN,L_SCAN,"Could not open %s for reading\n",file);
+	return -1;
+    }
+
+    fseek(infile,0,SEEK_END);
+    pmp3->file_size = ftell(infile);
+    fseek(infile,0,SEEK_SET);
+
+    rl = fread(hdr, 1, 44, infile);
+    fclose(infile);
+    if (rl != 44) {
+	DPRINTF(E_WARN,L_SCAN,"Could not read wav header from %s\n",file);
+        return -1;
+    }
+
+    if (strncmp(hdr + 0, "RIFF", 4) ||
+	strncmp(hdr + 8, "WAVE", 4) ||
+	strncmp(hdr + 12, "fmt ", 4) ||
+	strncmp(hdr + 36, "data", 4)) {
+	DPRINTF(E_WARN,L_SCAN,"Invalid wav header in %s\n",file);
+        return -1;
+    }
+
+    chunk_data_length = GET_WAV_INT32(hdr + 4);
+    format_data_length = GET_WAV_INT32(hdr + 16);
+    compression_code = GET_WAV_INT16(hdr + 20);
+    channel_count = GET_WAV_INT16(hdr + 22);
+    sample_rate = GET_WAV_INT32(hdr + 24);
+    sample_bit_length = GET_WAV_INT16(hdr + 34);
+    data_length = GET_WAV_INT32(hdr + 40);
+
+    if ((format_data_length != 16) ||
+	(compression_code != 1) ||
+	(channel_count < 1)) {
+	DPRINTF(E_WARN,L_SCAN,"Invalid wav header in %s\n",file);
+        return -1;
+    }
+
+    bit_rate = sample_rate * channel_count * ((sample_bit_length + 7) / 8) * 8;
+    pmp3->bitrate = bit_rate / 1000;
+    pmp3->samplerate = sample_rate;
+    sec = data_length / (bit_rate / 8);
+    ms = ((data_length % (bit_rate / 8)) * 1000) / (bit_rate / 8);
+    pmp3->song_length = (sec * 1000) + ms;
+
+    return 0;
+}
+
 
 /**
  * Decode an mp3 frame header.  Determine layer, bitrate, 
diff -ruNw mt-daapd-cvs-20050223/src/ogg.c mt-daapd-cvs-20050223-tri/src/ogg.c
--- mt-daapd-cvs-20050223/src/ogg.c	2004-12-06 05:06:12.000000000 +0200
+++ mt-daapd-cvs-20050223-tri/src/ogg.c	2005-02-24 17:59:02.457457164 +0200
@@ -780,6 +780,11 @@
     return 0;
 }
 
+int scan_get_oggtags(char *filename, MP3FILE *pmp3)
+{
+  return 0; /* Implement me! XXX */
+}
+
 /*int main(int argc, char **argv) {
     int f, ret;
 
diff -ruNw mt-daapd-cvs-20050223/src/ssc.c mt-daapd-cvs-20050223-tri/src/ssc.c
--- mt-daapd-cvs-20050223/src/ssc.c	2005-02-21 10:10:36.000000000 +0200
+++ mt-daapd-cvs-20050223-tri/src/ssc.c	2005-02-24 08:32:54.885234047 +0200
@@ -134,7 +134,7 @@
  * @param path char * to the real filename.
  * @param offset off_t to the point in file where the streaming starts.
  */
-FILE *server_side_convert_open(char *path, off_t offset)
+FILE *server_side_convert_open(char *path, off_t offset, unsigned long len_ms)
 {
     char *cmd;
     FILE *f;
@@ -142,8 +142,8 @@
     cmd=(char *)malloc(strlen(config.ssc_prog) +
 		       strlen(path) +
 		       64);
-    sprintf(cmd, "%s \"%s\" %ld",
-	    config.ssc_prog, path, (long)offset);
+    sprintf(cmd, "%s \"%s\" %ld %lu.%03lu",
+	    config.ssc_prog, path, (long)offset, len_ms / 1000, len_ms % 1000);
     f = popen(cmd, "r");
     return f;
 }
diff -ruNw mt-daapd-cvs-20050223/src/ssc.h mt-daapd-cvs-20050223-tri/src/ssc.h
--- mt-daapd-cvs-20050223/src/ssc.h	2005-02-21 10:10:37.000000000 +0200
+++ mt-daapd-cvs-20050223-tri/src/ssc.h	2005-02-24 08:32:54.886233889 +0200
@@ -27,7 +27,9 @@
 
 extern int server_side_convert_set(MP3FILE *pmp3);
 extern char *server_side_convert_path(char *path);
-extern FILE *server_side_convert_open(char *path, off_t offset);
+extern FILE *server_side_convert_open(char *path,
+				      off_t offset,
+				      unsigned long len_ms);
 extern void server_side_convert_close(FILE *f);
 
 #endif /* _SCC_H_ */
