patches/gst-plugins-good-02-cdda.diff
author Jon Tibble <meths@btinternet.com>
Sat, 06 Oct 2012 16:11:50 +0100
branchs11express-2010-11
changeset 22109 db10202d5f6d
parent 15397 afba4c1627d1
permissions -rw-r--r--
Added tag oi_151a_prestable7 for changeset 25dee50cecca

--- gst-plugins-good-0.10.13/configure.ac-orig	2009-01-23 10:02:53.003952000 -0600
+++ gst-plugins-good-0.10.13/configure.ac	2009-01-23 10:03:03.048601000 -0600
@@ -660,6 +668,24 @@ AG_GST_CHECK_FEATURE(CAIRO, [Cairo graph
   AG_GST_PKG_CHECK_MODULES(CAIRO, cairo >= 1.0.0)
 ])
 
+dnl *** cdda2wav ***
+translit(dnm, m, l) AM_CONDITIONAL(USE_CDDA2WAV, true)
+AG_GST_CHECK_FEATURE(CDDA2WAV, [HAL libraries], cdda2wav, [
+  AG_GST_PKG_CHECK_MODULES(CDDA2WAV, [hal >= 0.5.8, hal-storage >= 0.5.8, dbus-1 >= 0.32])
+  AC_PATH_PROG(CDDA2WAVBIN, cdda2wav, no)
+  if test x$CDDA2WAVBIN = xno; then
+    HAVE_CDDA2WAV="no"
+  else
+    CDDA_VERSION=`$CDDA2WAVBIN --version | grep Schilling`
+    if test -z "$CDDA_VERSION"; then
+      HAVE_CDDA2WAV="no"
+    else
+      HAVE_CDDA2WAV="yes"
+    fi
+  fi
+  AC_SUBST(HAVE_CDDA2WAV)
+])
+
 dnl **** ESound ****
 translit(dnm, m, l) AM_CONDITIONAL(USE_ESD, true)
 AG_GST_CHECK_FEATURE(ESD, [ESounD sound daemon], esdsink, [
@@ -928,6 +954,7 @@ AM_CONDITIONAL(USE_DIRECTDRAW, false)
 AM_CONDITIONAL(USE_DIRECTSOUND, false)
 AM_CONDITIONAL(USE_DV1394, false)
 AM_CONDITIONAL(USE_ESD, false)
+AM_CONDITIONAL(USE_CDDA2WAV, false)
 AM_CONDITIONAL(USE_FLAC, false)
 AM_CONDITIONAL(USE_GCONF, false)
 AM_CONDITIONAL(USE_GCONFTOOL, false)
@@ -1055,6 +1083,7 @@ ext/Makefile
 ext/aalib/Makefile
 ext/annodex/Makefile
 ext/cairo/Makefile
+ext/cdda2wav/Makefile
 ext/dv/Makefile
 ext/esd/Makefile
 ext/flac/Makefile
--- gst-plugins-good-0.10.10/ext/Makefile.am-orig	2008-08-27 23:56:51.987944000 -0500
+++ gst-plugins-good-0.10.10/ext/Makefile.am	2008-08-27 23:57:20.124741000 -0500
@@ -16,6 +16,12 @@ else
 CAIRO_DIR =
 endif
 
+if USE_CDDA2WAV
+CDDA2WAV_DIR = cdda2wav
+else
+CDDA2WAV_DIR =
+endif
+
 if USE_ESD
 ESD_DIR = esd
 else
@@ -131,6 +137,7 @@ SUBDIRS = \
 	$(CAIRO_DIR) \
 	$(DV1394_DIR) \
 	$(ESD_DIR) \
+	$(CDDA2WAV_DIR) \
 	$(FLAC_DIR) \
 	$(GCONF_DIR) \
 	$(GDK_PIXBUF_DIR) \
--- /dev/null	2007-03-19 01:47:57.000000000 +0800
+++ gst-plugins-good-0.10.6/ext/cdda2wav/Makefile.am	2007-03-19 01:46:41.639278000 +0800
@@ -0,0 +1,20 @@
+plugin_LTLIBRARIES = libgstcdda2wav.la
+
+libgstcdda2wav_la_SOURCES = \
+	gstcdda2wav.c
+
+libgstcdda2wav_la_CFLAGS = \
+	$(GST_PLUGINS_BASE_CFLAGS) \
+	$(GST_BASE_CFLAGS) \
+	$(GST_CFLAGS) \
+	$(CDDA2WAV_CFLAGS)
+
+libgstcdda2wav_la_LIBADD = \
+	$(GST_PLUGINS_BASE_LIBS) -lgstcdda-$(GST_MAJORMINOR) \
+	$(GST_BASE_LIBS) \
+	$(CDDA2WAV_LIBS)
+
+libgstcdda2wav_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) 
+
+noinst_HEADERS = \
+	gstcdda2wav.h
--- /dev/null	2008-12-19 17:50:06.000000000 -0600
+++ gst-plugins-good-0.10.6/ext/cdda2wav/gstcdda2wav.h	2008-07-17 07:48:57.000000000 -0500
@@ -0,0 +1,72 @@
+/* GStreamer
+ * Copyright (C) 2007 Brian Cameron <[email protected]> and
+ *                    Artem Kachitchkine <[email protected]>
+ * Copyright (C) 2008 J�rg Schilling <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_CDDA2WAV_H__
+#define __GST_CDDA2WAV_H__
+
+#include <sys/types.h>
+#include "gst/cdda/gstcddabasesrc.h"
+
+#define CDDA2WAV_BSIZE   2352
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_CDDA2WAV           (gst_cdda2wav_get_type())
+#define GST_CDDA2WAV(obj)           (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CDDA2WAV,GstCdda2Wav))
+#define GST_CDDA2WAV_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CDDA2WAV,GstCdda2WavClass))
+#define GST_IS_CDDA2WAV(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CDDA2WAV))
+#define GST_IS_CDDA2WAV_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CDDA2WAV))
+#define GST_CDDA2WAV_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CDDA2WAV, GstCdda2WavClass))
+
+typedef struct _GstCdda2Wav GstCdda2Wav;
+typedef struct _GstCdda2WavClass GstCdda2WavClass;
+
+#define CDDA2WAV_BSIZE   2352
+
+/**
+ * GstCdda2Wav::
+ *
+ * The cdda2wav object structure.
+ */
+struct _GstCdda2Wav {
+  GstCddaBaseSrc   cddabasesrc;
+
+  /*< private >*/
+  FILE            *cdda2wav_in;
+  FILE            *cdda2wav_out;
+  FILE            *cdda2wav_err;
+  pid_t		  cdda2wav_pid;
+  uint_t          track_count;
+  char            **devices;
+  char            *current_device;
+  boolean_t       *has_audio;
+};
+
+struct _GstCdda2WavClass {
+  GstCddaBaseSrcClass parent_class;
+};
+
+GType    gst_cdda2wav_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_CDDA2WAV_H__ */
+
--- /dev/null	2009-01-08 02:18:21.000000000 -0600
+++ gst-plugins-good-0.10.11/ext/cdda2wav/gstcdda2wav.c	2009-01-08 02:17:15.164536000 -0600
@@ -0,0 +1,795 @@
+/* GStreamer
+ * Copyright (C) <2007> Brian Cameron <[email protected]> and
+ *                      Artem Kachitchkine <[email protected]>
+ * Copyright (C) 2008	J�rg Schilling <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:element-cdda2wav
+ * @short_description: Reads raw audio from an Audio CD
+ * @see_also: GstCddaBaseSrc
+ *
+ * <refsect2>
+ * <para>
+ * cdda2wav reads and extracts raw audio from Audio CDs using cdda2wav.
+ * </para>
+ * <title>Example launch line</title>
+ * <para>
+ * <programlisting>
+ * gst-launch cdda2wav track=5 ! audioconvert ! vorbisenc ! oggmux ! filesink location=track5.ogg
+ * </programlisting>
+ * This pipeline extracts track 5 of the audio CD and encodes it into an
+ * Ogg/Vorbis file.
+ * </para>
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <libintl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/filio.h>
+#include <signal.h>
+#define	HAVE_POLL		/* XXX Need autoconf test */
+#ifdef	HAVE_POLL
+#include <poll.h>
+#endif
+#include <sys/byteorder.h>
+#include <hal/libhal.h>
+#include <hal/libhal-storage.h>
+
+#include "gstcdda2wav.h"
+#include "gst/gst-i18n-plugin.h"
+
+enum
+{
+  PROP0 = 0,
+};
+
+GST_DEBUG_CATEGORY_STATIC (gst_cdda2wav_debug);
+#define GST_CAT_DEFAULT gst_cdda2wav_debug
+
+GST_BOILERPLATE (GstCdda2Wav, gst_cdda2wav, GstCddaBaseSrc,
+    GST_TYPE_CDDA_BASE_SRC)
+
+static void gst_cdda2wav_finalize (GObject * obj);
+static GstBuffer *gst_cdda2wav_read_sector (GstCddaBaseSrc * src,
+    gint sector);
+static gboolean gst_cdda2wav_open (GstCddaBaseSrc * src,
+    const gchar * device);
+static void gst_cdda2wav_close (GstCddaBaseSrc * src);
+static gchar **gst_cdda2wav_probe_devices (GstCddaBaseSrc * cddabasesrc);
+static gchar *gst_cdda2wav_get_default_device (GstCddaBaseSrc * cddabasesrc);
+static void gst_cdda2wav_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_cdda2wav_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+static void gst_cdda2wav_hal_find_cdroms (GstCdda2Wav *);
+
+static pid_t xpopen(FILE **pfpin, FILE **pfpout, FILE **pfperr, const char *cmd);
+static int xpclose(FILE *fpin, FILE *fpout, FILE *fperr, pid_t child);
+
+
+static const GstElementDetails cdda2wav_details =
+GST_ELEMENT_DETAILS ("CD Audio (cdda) Source",
+    "Source/File",
+    "Read audio from CD",
+    "Brian Cameron <[email protected]>, " "Artem Kachitchkine <[email protected]>, "
+    "Joerg Schilling <[email protected]>");
+
+static void
+gst_cdda2wav_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_set_details (element_class, &cdda2wav_details);
+}
+
+static void
+gst_cdda2wav_init (GstCdda2Wav * src, GstCdda2WavClass * klass)
+{
+  src->devices        = NULL;
+  src->current_device = NULL;
+  src->cdda2wav_in    = NULL;
+  src->cdda2wav_out   = NULL;
+  src->cdda2wav_err   = NULL;
+  src->cdda2wav_pid   = 0;
+}
+
+static void
+gst_cdda2wav_class_init (GstCdda2WavClass * klass)
+{
+  GstCddaBaseSrcClass *cddabasesrc_class = GST_CDDA_BASE_SRC_CLASS (klass);
+  GObjectClass *gobject_class            = G_OBJECT_CLASS (klass);
+
+  gobject_class->set_property            = gst_cdda2wav_set_property;
+  gobject_class->get_property            = gst_cdda2wav_get_property;
+  gobject_class->finalize                = gst_cdda2wav_finalize;
+
+  cddabasesrc_class->open                = gst_cdda2wav_open;
+  cddabasesrc_class->close               = gst_cdda2wav_close;
+  cddabasesrc_class->read_sector         = gst_cdda2wav_read_sector;
+  cddabasesrc_class->get_default_device  = gst_cdda2wav_get_default_device;
+  cddabasesrc_class->probe_devices       = gst_cdda2wav_probe_devices;
+}
+
+static gchar **
+gst_cdda2wav_probe_devices (GstCddaBaseSrc * cddabasesrc)
+{
+  GstCdda2Wav *src = GST_CDDA2WAV (cddabasesrc);
+
+  if (src->devices != NULL) {
+     free (src->devices);
+     free (src->has_audio);
+
+     src->devices = NULL;
+     src->has_audio = NULL;
+  }
+  gst_cdda2wav_hal_find_cdroms (src);
+
+  return (src->devices);
+}
+
+static gchar *
+gst_cdda2wav_get_default_device (GstCddaBaseSrc * cddabasesrc)
+{
+  GstCdda2Wav *src;
+  gchar *ret = NULL;
+  int i;
+
+  src = GST_CDDA2WAV (cddabasesrc);
+
+  if (src->devices == NULL) {
+     gst_cdda2wav_probe_devices (cddabasesrc);
+     if (src->devices == NULL) {
+        return (NULL);
+     }
+  }
+
+  /* prefer a drive with an audio disc, otherwise pick first */
+  for (i = 0; src->devices[i] != NULL; i++) {
+      if (src->has_audio[i]) {
+         ret = src->devices[i];
+         break;
+      }
+  }
+
+  if (ret == NULL)
+     ret = src->devices[0];
+
+  GST_LOG_OBJECT (src, "returning default device: %s", GST_STR_NULL (ret));
+
+  return (ret);
+}
+
+static boolean_t
+cdda2wav_read_toc (GstCdda2Wav *src)
+{
+  gchar   *command;
+  char     buf[BUFSIZ];
+  guint    leadout;
+  int      track_num;
+  uint_t   i;
+  GstCddaBaseSrcTrack tracks[100];
+  FILE	*f;
+
+  /* Open the connection to cdda2wav */
+  command = g_strdup_printf (
+	"/usr/bin/cdda2wav -q dev=%s --info-only -vtoc --no-infofile --gui 2>&1",
+	src->current_device);
+  f = popen (command, "r");
+  g_free (command);
+	if (f == NULL)
+		return (FALSE);
+
+  track_num = 0;
+  leadout   = 0;
+
+  i = 0;
+  while (fgets (buf, BUFSIZ, f) != NULL) {
+     char *endptr;
+     g_strchomp (buf);
+
+     if ((strlen (buf) > 12) &&
+         (buf[0] == 'T') &&
+         (g_ascii_isdigit(buf[1])) &&
+         (g_ascii_isdigit(buf[2])) &&
+         (buf[3] == ':')) {
+
+	track_num = buf[2] - '0' + (buf[1] - '0') * 10;
+	tracks[i].is_audio = strstr((buf + 4), "audio") != 0;
+	tracks[i].num      = buf[2] - '0' + (buf[1] - '0') * 10;
+	tracks[i].tags     = NULL;
+	tracks[i].start    = (guint) (g_ascii_strtod ((buf + 4), &endptr));
+#ifdef	DEBUG
+	fprintf(stderr, "Track %2.2d %2.2d audio %d start %u\n",
+		i, tracks[i].num, tracks[i].is_audio, tracks[i].start);
+#endif
+	i++;
+     }
+
+     if (strncmp (buf, "Leadout:", 8) == 0) {
+         leadout = (guint) (g_ascii_strtod ((buf + 8), &endptr));
+     }
+  }
+  track_num = i;
+
+  for (i=0; i < track_num; i++) {
+     GstCddaBaseSrcTrack track = { 0, };
+
+     track.is_audio = tracks[i].is_audio;
+     track.num      = tracks[i].num;
+     track.tags     = NULL;
+     track.start    = tracks[i].start;
+
+     if (i < (track_num - 1))
+        track.end = tracks[i+1].start - 1;
+     else
+        track.end = leadout;
+
+     gst_cdda_base_src_add_track (GST_CDDA_BASE_SRC (src), &track);
+  }
+
+  src->track_count = track_num + 1;
+
+  pclose (f);
+
+  return (TRUE);
+}
+
+static gboolean
+gst_cdda2wav_open (GstCddaBaseSrc * cddabasesrc, const gchar * device)
+{
+  GstCdda2Wav *src = GST_CDDA2WAV (cddabasesrc);
+  gchar       *command;
+  gboolean     ret;
+
+  g_assert (device != NULL);
+
+  GST_LOG_OBJECT (src, "Trying to open device %s", device);
+  src->current_device = g_strdup (device);
+
+  ret = cdda2wav_read_toc (src);
+
+  return ret;
+}
+
+static int
+gst_cdda_close (GstCdda2Wav  * src)
+{
+  int rc = 0;
+
+  if (src->cdda2wav_pid != 0) {
+     rc = xpclose (src->cdda2wav_in, src->cdda2wav_out, src->cdda2wav_err,
+		src->cdda2wav_pid);
+  }
+  src->cdda2wav_in  = NULL;
+  src->cdda2wav_out = NULL;
+  src->cdda2wav_err = NULL;
+  src->cdda2wav_pid = 0;
+
+  return rc;
+}
+
+static void
+gst_cdda2wav_close (GstCddaBaseSrc * cddabasesrc)
+{
+  GstCdda2Wav *src = GST_CDDA2WAV (cddabasesrc);
+
+  gst_cdda_close (src);
+}
+
+static gboolean
+getans(GstCdda2Wav *src)
+{
+	char	buf[1024];
+	char	*p;
+	int	ret;
+#ifdef	HAVE_POLL
+	struct pollfd	pfd[1];
+#endif
+
+#ifdef	DEBUG
+	fprintf(stderr, "getans\n");
+#endif
+	if (src->cdda2wav_err == NULL)
+		return (FALSE);
+	do {
+		buf[0] = '\0';
+#ifdef	HAVE_POLL
+		pfd[0].fd = fileno(src->cdda2wav_err);
+		pfd[0].events = POLLIN;
+		pfd[0].revents = 0;
+        	if (poll(pfd, 1, 5000) < 1)	/* Give up after 5s */
+			break;
+#else
+		insert select code here
+#endif
+		p = fgets(buf, sizeof (buf), src->cdda2wav_err);
+#ifdef	DEBUG
+		fprintf(stderr, "ERR: '%s'\n", buf);
+#endif
+		if (buf[0] >= '0' && buf[0] <= '9' &&
+		    buf[1] >= '0' && buf[1] <= '9' &&
+		    buf[2] >= '0' && buf[2] <= '9' &&
+		    buf[3] == ' ')
+			break;
+	} while (p);
+	ret = atoi(buf);
+	if (ret != 200) {
+		fprintf(stderr, "ERR: %s\n", buf);
+		gst_cdda_close (src);
+		return (FALSE);
+	}
+	return (TRUE);
+}
+
+static void
+flushpipe(FILE *f)
+{
+	int	nread;
+	char	fbuf[5120];
+
+	do {
+		ioctl(fileno(f), FIONREAD, &nread);
+		if (nread > 0) {
+			int	amt;
+
+			amt = nread;
+			if (amt > sizeof (fbuf))
+				amt = sizeof (fbuf);
+			if (fread(fbuf, amt, 1, f) != 1)
+				break;
+		}
+	} while (nread > 0);
+}
+
+static gboolean
+sendcmd(GstCdda2Wav *src, const char *fmt, uint_t addr)
+{
+	struct sigaction old;
+	struct sigaction my;
+
+	sigemptyset(&my.sa_mask);
+	my.sa_handler == SIG_IGN;
+	my.sa_flags = 0;
+	sigaction(SIGPIPE, &my, &old);
+
+	clearerr(src->cdda2wav_in);
+	fprintf(src->cdda2wav_in, fmt, addr);
+	fflush(src->cdda2wav_in);
+
+	sigaction(SIGPIPE, &old, NULL);
+
+	if (ferror(src->cdda2wav_in)) {
+		gst_cdda_close(src);
+		return (FALSE);
+	}
+	return (TRUE);
+}
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define CDDA2WAV_BYTE_ORDER "little"
+#elif G_BYTE_ORDER == G_BIG_ENDIAN
+#define CDDA2WAV_BYTE_ORDER "big"
+#else
+#error "G_BYTE_ORDER should be big or little endian."
+#endif
+
+static gboolean
+cdda2wav_read (GstCdda2Wav *src, void *buf, uint_t addr, uint_t len)
+{
+  static int last_addr = -1; 
+  char * command;
+  int		did_retry = -1;
+  gboolean	do_paranoia = FALSE;	/* Need to be parameterized from elsewhere */
+
+retry:
+  if (++did_retry > 1)
+	return (FALSE);
+
+  if (src->cdda2wav_pid == 0) {
+     command = g_strdup_printf (
+	"/usr/bin/cdda2wav -q --output-endianess=%s dev=%s -Oraw %s -interactive -",
+        CDDA2WAV_BYTE_ORDER, src->current_device,
+        do_paranoia ? "-paranoia -paraopts=minoverlap=2":"");
+     src->cdda2wav_pid = xpopen (&src->cdda2wav_in, &src->cdda2wav_out,
+						&src->cdda2wav_err, command);
+#ifdef	DEBUG
+     fprintf(stderr, "PID %d\n", src->cdda2wav_pid);
+#endif
+     g_free (command);
+	if (!sendcmd(src, "read sectors %u\n", addr))
+		goto retry;
+	if (!getans(src))
+		goto retry;
+	last_addr = addr;
+  } else if (addr != (last_addr + 1)) {	/* Check whether we need to seek */
+	int	nread;
+
+	if (!sendcmd(src, "stop\n", 0))
+		goto retry;
+	flushpipe(src->cdda2wav_out);
+	if (!getans(src))
+		goto retry;
+	do {
+		if (!sendcmd(src, "stop\n", 0))
+			goto retry;
+		flushpipe(src->cdda2wav_out);
+		if (!getans(src))
+			goto retry;
+		ioctl(fileno(src->cdda2wav_out), FIONREAD, &nread);
+	} while (nread > 0);
+	if (!sendcmd(src, "read sectors %u\n", addr))
+		goto retry;
+	if (!getans(src))
+		goto retry;
+  }
+  last_addr = addr;
+
+  if (fread (buf, CDDA2WAV_BSIZE, len, src->cdda2wav_out) != len) {
+	if (feof(src->cdda2wav_out)) {
+		if (gst_cdda_close (src) == 0)
+			return (TRUE);
+		goto retry;
+	}
+	return (FALSE);
+  }
+
+  return (TRUE);
+}
+
+static GstBuffer *
+gst_cdda2wav_read_sector (GstCddaBaseSrc * cddabasesrc, gint sector)
+{
+  GstCdda2Wav *src = GST_CDDA2WAV (cddabasesrc);
+  size_t       len = CDDA2WAV_BSIZE;
+  GstBuffer   *buf;
+
+  buf = gst_buffer_new_and_alloc (CDDA2WAV_BSIZE);
+
+  if (!cdda2wav_read (src, GST_BUFFER_DATA (buf), sector, 1)) {
+     free (buf);
+     buf = NULL;
+     return NULL;
+  }
+
+  return (buf);
+}
+
+static void
+gst_cdda2wav_finalize (GObject * obj)
+{
+  GstCdda2Wav *src = GST_CDDA2WAV (obj);
+
+  gst_cdda_close (src);
+
+  g_free (src->devices);
+  g_free (src->current_device);
+
+  G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_cdda2wav_debug, "cdda2wav", 0,
+      "CDDA Source");
+
+  if (!gst_element_register (plugin, "cdda2wav", GST_RANK_SECONDARY,
+          GST_TYPE_CDDA2WAV))
+    return FALSE;
+
+#ifdef ENABLE_NLS
+  GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
+      LOCALEDIR);
+  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+#endif
+
+  return TRUE;
+}
+
+static void
+gst_cdda2wav_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstCdda2Wav *src = GST_CDDA2WAV (object);
+
+  switch (prop_id) {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_cdda2wav_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstCdda2Wav *src = GST_CDDA2WAV (object);
+
+  switch (prop_id) {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    "cdda2wav",
+    "Read audio from CD",
+    plugin_init, VERSION, "GPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
+
+/* ===== HAL stuff ===== */
+
+static LibHalContext *
+gst_cdda2wav_hal_init (GstCdda2Wav *src)
+{
+  DBusError error;
+  LibHalContext *hal_ctx = NULL;
+
+  dbus_error_init (&error);
+  if ((hal_ctx = libhal_ctx_new ()) == NULL) {
+     GST_ELEMENT_ERROR (src, LIBRARY, INIT,
+        ("failed to initialize HAL"), ("failed to initialize HAL"));
+     goto out;
+  }
+  if (!libhal_ctx_set_dbus_connection(hal_ctx,
+      dbus_bus_get (DBUS_BUS_SYSTEM, &error))) {
+
+     GST_ELEMENT_ERROR (src, LIBRARY, INIT,
+        ("DBus connection failed"),
+        ("libhal_ctx_set_dbus_connection: %s %s", error.name, error.message));
+     libhal_ctx_free (hal_ctx);
+     goto out;
+  }
+  if (!libhal_ctx_init(hal_ctx, &error)) {
+    GST_ELEMENT_ERROR (src, LIBRARY, INIT,
+       ("HAL context init failed"),
+       ("libhal_ctx_init: %s: %s", error.name, error.message));
+    libhal_ctx_free (hal_ctx);
+    goto out;
+  }
+out:
+  dbus_error_free (&error);
+
+  return (hal_ctx);
+}
+
+static void
+gst_cdda2wav_hal_fini (LibHalContext *hal_ctx)
+{
+  DBusError error;
+
+  dbus_error_init (&error);
+  libhal_ctx_shutdown (hal_ctx, &error);
+  libhal_ctx_free (hal_ctx);
+  dbus_error_free (&error);
+}
+
+static void
+gst_cdda2wav_hal_find_cdroms (GstCdda2Wav *src)
+{
+  DBusError       error;
+  LibHalContext   *hal_ctx;
+  int             i, j, k;
+  char            **drive_udis, **volume_udis;
+  int             num_drives, num_volumes;
+  char            *raw_device;
+  LibHalDrive     *drive;
+  dbus_bool_t     has_audio;
+
+  if ((hal_ctx = gst_cdda2wav_hal_init (src)) == NULL) {
+     return;
+  }
+  dbus_error_init (&error);
+
+  drive_udis = libhal_find_device_by_capability (hal_ctx, "storage.cdrom",
+    &num_drives, &error);
+
+  if (dbus_error_is_set (&error) || drive_udis == NULL) {
+     goto out;
+  }
+
+  src->devices   = calloc (num_drives + 1, sizeof (char *));
+  src->has_audio = calloc (num_drives + 1, sizeof (boolean_t));
+
+  for (i = j = 0; i < num_drives; i++) {
+     raw_device = libhal_device_get_property_string (hal_ctx,
+        drive_udis[i], "block.solaris.raw_device", &error);
+     dbus_error_free (&error);
+
+     if ((raw_device == NULL) || (strlen (raw_device) == 0)) {
+        libhal_free_string (raw_device);
+        continue;
+     }
+
+     src->devices[j] = strdup (raw_device);
+     libhal_free_string (raw_device);
+
+     /* check for audio disc in this drive */
+     if ((drive = libhal_drive_from_udi (hal_ctx, drive_udis[i])) != NULL) {
+        if ((volume_udis = libhal_drive_find_all_volumes (hal_ctx,
+             drive, &num_volumes)) != NULL) {
+
+           for (k = 0; k < num_volumes; k++) {
+               src->has_audio[j] = (boolean_t)
+               libhal_device_get_property_bool (hal_ctx,
+                  volume_udis[k], "volume.disc.has_audio", &error);
+               dbus_error_free (&error);
+               if (src->has_audio[j]) {
+                  break;
+               }
+           }
+           libhal_free_string_array (volume_udis);
+        }
+        libhal_drive_free (drive);
+     }
+
+     j++;
+  }
+
+out:
+  libhal_free_string_array (drive_udis);
+  dbus_error_free (&error);
+
+  gst_cdda2wav_hal_fini (hal_ctx);
+}
+
+#define	HAVE_SYS_FORK_H		/* XXX Need autoconf test */
+#ifdef	HAVE_SYS_FORK_H
+#include <sys/fork.h>
+#endif
+#include <wait.h>
+
+static pid_t
+xpopen(FILE **pfpin, FILE **pfpout, FILE **pfperr, const char *cmd)
+{
+	int	in[2];
+	int	out[2];
+	int	err[2];
+	pid_t	pid;
+ 
+	in[0]  = STDIN_FILENO;
+	out[1] = STDOUT_FILENO;
+	err[1] = STDERR_FILENO;
+	if (pfpin) {
+		if (pipe(in) < 0)
+			return ((pid_t)-1);
+	}
+	if (pfpout) {
+		if (pipe(out) < 0) {
+			close(in[0]);
+			close(in[1]);
+			return ((pid_t)-1);
+		}
+	}
+	if (pfperr) {
+		if (pipe(err) < 0) {
+			close(in[0]);
+			close(in[1]);
+			close(out[0]);
+			close(out[1]);
+			return ((pid_t)-1);
+		}
+	}
+
+#ifdef	FORK_NOSIGCHLD
+	pid = vforkx(FORK_NOSIGCHLD|FORK_WAITPID);
+#else
+	pid = vfork();	/* Need to find a way to deal with SIGCHLD and wait */
+#endif
+	if (pid == (pid_t)-1) {
+		return ((pid_t)-1);
+	} else if (pid > 0) {
+		if (pfpin) {
+			close(in[0]);
+			*pfpin = fdopen(in[1], "w");
+			if (*pfpin == NULL) {
+				close(in[1]);
+				close(out[0]);
+				close(out[1]);
+				close(err[0]);
+				close(err[1]);
+				kill(pid, SIGKILL);
+				xpclose(NULL, NULL, NULL, pid);
+				return ((pid_t)-1);
+			}
+		}
+		if (pfpout) {
+			close(out[1]);
+			*pfpout = fdopen(out[0], "r");
+			if (*pfpout == NULL) {
+				close(out[0]);
+				close(err[0]);
+				close(err[1]);
+				kill(pid, SIGKILL);
+				xpclose(*pfpin, NULL, NULL, pid);
+				return ((pid_t)-1);
+			}
+		}
+		if (pfperr) {
+			close(err[1]);
+			*pfperr = fdopen(err[0], "r");
+			if (*pfperr == NULL) {
+				close(err[0]);
+				kill(pid, SIGKILL);
+				xpclose(*pfpin, *pfpout, NULL, pid);
+				return ((pid_t)-1);
+			}
+		}
+	} else {
+		if (pfpin)
+			close(in[1]);
+		if (pfpout)
+			close(out[0]);
+		if (pfperr)
+			close(err[0]);
+		if (in[0] != STDIN_FILENO) {
+			dup2(in[0], STDIN_FILENO);
+			close(in[0]);
+		}
+		if (out[1] != STDOUT_FILENO) { 
+			dup2(out[1], STDOUT_FILENO); 
+			close(out[1]); 
+		} 
+		if (err[1] != STDERR_FILENO) {
+			dup2(err[1], STDERR_FILENO);
+			close(err[1]);
+		}
+		execl("/bin/sh", "sh", "-c", cmd, (char *)0);
+		_exit(1);
+	}
+	return (pid);
+}
+
+static int
+xpclose(FILE *fpin, FILE *fpout, FILE *fperr, pid_t child)
+{
+	int	status = 0;
+
+	if (fpin)
+		fclose(fpin);
+	if (fpout)
+		fclose(fpout);
+	if (fperr)
+		fclose(fperr);
+
+	if (child <= 0) {
+		errno = ECHILD;
+		return (-1);
+	}
+	if (waitpid(child, &status, WNOHANG) == child)
+		return (status);
+	while (waitpid(child, &status, 0) < 0) {
+		if (errno != EINTR) {
+			status = -1;
+			break;
+		}
+	}
+	return (status);
+}