[Goodies-commits] r5777 - xfburn/trunk/xfburn
David Mohr
squisher at xfce.org
Thu Oct 23 18:44:07 CEST 2008
Author: squisher
Date: 2008-10-23 16:44:06 +0000 (Thu, 23 Oct 2008)
New Revision: 5777
Modified:
xfburn/trunk/xfburn/xfburn-audio-composition.c
xfburn/trunk/xfburn/xfburn-burn-audio-cd-composition-dialog.c
xfburn/trunk/xfburn/xfburn-error.h
xfburn/trunk/xfburn/xfburn-global.h
xfburn/trunk/xfburn/xfburn-main.c
xfburn/trunk/xfburn/xfburn-transcoder-basic.c
xfburn/trunk/xfburn/xfburn-transcoder-gst.c
xfburn/trunk/xfburn/xfburn-transcoder.c
xfburn/trunk/xfburn/xfburn-transcoder.h
Log:
Initial checkin for gstreamer code. Does NOT work yet.
Modified: xfburn/trunk/xfburn/xfburn-audio-composition.c
===================================================================
--- xfburn/trunk/xfburn/xfburn-audio-composition.c 2008-10-23 12:51:56 UTC (rev 5776)
+++ xfburn/trunk/xfburn/xfburn-audio-composition.c 2008-10-23 16:44:06 UTC (rev 5777)
@@ -87,12 +87,6 @@
typedef enum
{
- NOT_ADDING_EXT,
- NOT_ADDING_FMT,
-} XfburnNotAddingReason;
-
-typedef enum
-{
AUDIO_COMPOSITION_TYPE_RAW,
} AudioCompositionEntryType;
@@ -177,14 +171,13 @@
gchar *selected_files;
GtkTreePath *path_where_insert;
- XfburnNotAddingReason warned_about_not_adding;
-
GdkDragContext * dc;
gboolean success;
gboolean del;
guint32 time;
void *thread_params;
+ GHashTable *warned_about;
GtkActionGroup *action_group;
GtkUIManager *ui_manager;
@@ -201,6 +194,7 @@
/* globals */
static GtkHPanedClass *parent_class = NULL;
static guint instances = 0;
+static gchar *did_warn = "Did warn about this already";
static const GtkActionEntry action_entries[] = {
{"add-file", GTK_STOCK_ADD, N_("Add"), NULL, N_("Add the selected file(s) to the composition"),
@@ -466,6 +460,8 @@
gtk_action_set_sensitive (GTK_ACTION (action), FALSE);
priv->trans = xfburn_transcoder_get_global ();
+
+ priv->warned_about = g_hash_table_new (g_direct_hash, g_direct_equal);
}
static void
@@ -792,6 +788,8 @@
priv->thread_params = NULL;
}
+ g_hash_table_remove_all (priv->warned_about);
+
tracks_changed (dc);
xfburn_default_cursor (priv->content);
@@ -1040,30 +1038,24 @@
}
}
+/* This function ensures that the user is only told about once about each
+ * type of error. Useful for dragging folders into the audio composition view. */
static void
-notify_not_adding (XfburnAudioComposition * dc, XfburnNotAddingReason r, const gchar *path)
+notify_not_adding (XfburnAudioComposition * dc, GError *error)
{
XfburnAudioCompositionPrivate *priv = XFBURN_AUDIO_COMPOSITION_GET_PRIVATE (dc);
- if (!(priv->warned_about_not_adding & r)) {
- const gchar *str;
+ g_assert (error != NULL);
- priv->warned_about_not_adding |= r;
+ if (error->domain != XFBURN_ERROR) {
+ xfce_warn (error->message);
+ return;
+ }
- switch (r) {
- case NOT_ADDING_EXT:
- xfce_warn (_("At the moment only .wav files can get added to the compilation!"));
- break;
- case NOT_ADDING_FMT:
- xfce_warn (_("At the moment only CD quality uncompressed (pcm) .wav files can get added to the compilation!"));
- break;
- default:
- /* This should never happen... */
- str = _("Unsupported files cannot get added to the compilation!");
- xfce_warn (str);
- g_warning (str);
- break;
- }
+ if (g_hash_table_lookup (priv->warned_about, GINT_TO_POINTER (error->code)) == NULL) {
+ g_hash_table_insert (priv->warned_about, GINT_TO_POINTER (error->code), did_warn);
+
+ xfce_warn (error->message);
}
}
@@ -1163,7 +1155,8 @@
/* since we don't add the folder, the next song needs
* to get added after the last one from this directory */
- *iter = *iter_last;
+ if (iter_last != NULL)
+ *iter = *iter_last;
if (insertion == NULL)
g_free (iter_last);
@@ -1177,18 +1170,11 @@
atrack = xfburn_transcoder_get_audio_track (priv->trans, path, &error);
if (atrack == NULL) {
- XfburnNotAddingReason reason;
-
- if (error->code == XFBURN_ERROR_NOT_AUDIO_EXT)
- reason = NOT_ADDING_EXT;
- else if (error->code == XFBURN_ERROR_NOT_AUDIO_FORMAT)
- reason = NOT_ADDING_FMT;
-
- g_error_free (error);
-
gdk_threads_enter ();
- notify_not_adding (dc, reason, path);
+ notify_not_adding (dc, error);
gdk_threads_leave ();
+
+ g_error_free (error);
return FALSE;
}
@@ -1205,6 +1191,7 @@
gdk_threads_leave ();
/* (filesize - header_size) / bytes_per_seconds */
+ DBG ("length = %d", atrack->length);
secs = atrack->length;
humanlength = g_strdup_printf ("%2d:%2d", secs / 60, secs % 60);
Modified: xfburn/trunk/xfburn/xfburn-burn-audio-cd-composition-dialog.c
===================================================================
--- xfburn/trunk/xfburn/xfburn-burn-audio-cd-composition-dialog.c 2008-10-23 12:51:56 UTC (rev 5776)
+++ xfburn/trunk/xfburn/xfburn-burn-audio-cd-composition-dialog.c 2008-10-23 16:44:06 UTC (rev 5777)
@@ -508,6 +508,7 @@
int *track_sectors;
gboolean abort = FALSE;
XfburnTranscoder *trans;
+ GError *error = NULL;
struct burn_drive_info *drive_info = NULL;
@@ -529,7 +530,6 @@
track_list = params->tracks;
for (i=0; i<n_tracks; i++) {
XfburnAudioTrack *atrack = track_list->data;
- GError *error = NULL;
tracks[i] = xfburn_transcoder_create_burn_track (trans, atrack, &error);
if (tracks[i] == NULL) {
@@ -548,8 +548,14 @@
src_fifo = burn_fifo_source_new (src, 2048, xfburn_settings_get_int ("fifo-size", FIFO_DEFAULT_SIZE) / 2, 0);
burn_source_free (src);
*/
+ if (!abort) {
+ if (!xfburn_transcoder_prepare (trans, &error)) {
+ xfburn_progress_dialog_burning_failed (XFBURN_PROGRESS_DIALOG (dialog_progress), error->message);
+ g_error_free (error);
+ abort = TRUE;
+ }
+ }
-
if (!abort) {
if (!xfburn_device_grab (params->device, &drive_info)) {
xfburn_progress_dialog_burning_failed (XFBURN_PROGRESS_DIALOG (dialog_progress), _("Unable to grab drive"));
Modified: xfburn/trunk/xfburn/xfburn-error.h
===================================================================
--- xfburn/trunk/xfburn/xfburn-error.h 2008-10-23 12:51:56 UTC (rev 5776)
+++ xfburn/trunk/xfburn/xfburn-error.h 2008-10-23 16:44:06 UTC (rev 5777)
@@ -28,6 +28,7 @@
typedef enum
{
+ XFBURN_ERROR_NONE,
XFBURN_ERROR_NOT_IMPLEMENTED,
XFBURN_ERROR_STAT,
XFBURN_ERROR_NOT_AUDIO_EXT,
@@ -35,6 +36,10 @@
XFBURN_ERROR_COULD_NOT_OPEN_FILE,
XFBURN_ERROR_BURN_SOURCE,
XFBURN_ERROR_BURN_TRACK,
+ XFBURN_ERROR_GST_CREATION,
+ XFBURN_ERROR_GST_STATE,
+ XFBURN_ERROR_GST_TIMEOUT,
+ XFBURN_ERROR_PIPE,
} XfburnError;
#define XFBURN_ERROR xfburn_error_quark ()
Modified: xfburn/trunk/xfburn/xfburn-global.h
===================================================================
--- xfburn/trunk/xfburn/xfburn-global.h 2008-10-23 12:51:56 UTC (rev 5776)
+++ xfburn/trunk/xfburn/xfburn-global.h 2008-10-23 16:44:06 UTC (rev 5777)
@@ -44,6 +44,8 @@
#define FIFO_DEFAULT_SIZE 4096 /* in kb, as int */
+#define AUDIO_BYTES_PER_SECTORS 2352
+
#define PCM_BYTES_PER_SECS 176400
#endif
Modified: xfburn/trunk/xfburn/xfburn-main.c
===================================================================
--- xfburn/trunk/xfburn/xfburn-main.c 2008-10-23 12:51:56 UTC (rev 5776)
+++ xfburn/trunk/xfburn/xfburn-main.c 2008-10-23 16:44:06 UTC (rev 5777)
@@ -303,6 +303,14 @@
#else
transcoder = XFBURN_TRANSCODER (xfburn_transcoder_basic_new ());
#endif
+ if (!xfburn_transcoder_is_initialized (transcoder, &error)) {
+ g_warning ("Failed to initialize %s transcoder: %s\n\t(falling back to basic implementation)", xfburn_transcoder_get_name (transcoder), error->message);
+ g_error_free (error);
+ g_object_unref (transcoder);
+ transcoder = XFBURN_TRANSCODER (xfburn_transcoder_basic_new ());
+ } else {
+ g_message ("Using %s transcoder.", xfburn_transcoder_get_name (transcoder));
+ }
xfburn_transcoder_set_global (transcoder);
/* evaluate parsed command line options */
Modified: xfburn/trunk/xfburn/xfburn-transcoder-basic.c
===================================================================
--- xfburn/trunk/xfburn/xfburn-transcoder-basic.c 2008-10-23 12:51:56 UTC (rev 5776)
+++ xfburn/trunk/xfburn/xfburn-transcoder-basic.c 2008-10-23 16:44:06 UTC (rev 5777)
@@ -51,6 +51,9 @@
static void transcoder_interface_init (XfburnTranscoderInterface *iface, gpointer iface_data);
/* internals */
+static const gchar * get_name (XfburnTranscoder *trans);
+static gboolean is_initialized (XfburnTranscoder *trans, GError **error);
+
static XfburnAudioTrack * get_audio_track (XfburnTranscoder *trans, const gchar *fn, GError **error);
static gboolean has_audio_ext (const gchar *path);
static gboolean is_valid_wav (const gchar *path);
@@ -59,8 +62,6 @@
static struct burn_track * create_burn_track (XfburnTranscoder *trans, XfburnAudioTrack *atrack, GError **error);
static gboolean needs_swap (char header[44]);
-static gboolean free_burning_resources (XfburnTranscoder *trans, XfburnAudioTrack *atrack, GError **error);
-
#define XFBURN_TRANSCODER_BASIC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), XFBURN_TYPE_TRANSCODER_BASIC, XfburnTranscoderBasicPrivate))
enum {
@@ -145,14 +146,28 @@
static void
transcoder_interface_init (XfburnTranscoderInterface *iface, gpointer iface_data)
{
+ iface->get_name = get_name;
+ iface->is_initialized = is_initialized;
iface->get_audio_track = get_audio_track;
iface->create_burn_track = create_burn_track;
- iface->free_burning_resources = free_burning_resources;
}
/* */
/* internals */
/* */
+static const gchar *
+get_name (XfburnTranscoder *trans)
+{
+ return "basic";
+}
+
+static gboolean
+is_initialized (XfburnTranscoder *trans, GError **error)
+{
+ /* there is nothing to check, because there is nothing to initialize */
+ return TRUE;
+}
+
static XfburnAudioTrack *
get_audio_track (XfburnTranscoder *trans, const gchar *fn, GError **error)
{
@@ -161,18 +176,18 @@
if (!has_audio_ext (fn)) {
g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_NOT_AUDIO_EXT,
- "File %s does not have a .wav extension", fn);
+ _("File %s does not have a .wav extension"), fn);
return NULL;
}
if (!is_valid_wav (fn)) {
g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_NOT_AUDIO_FORMAT,
- "File %s does not contain uncompressed PCM wave audio", fn);
+ _("File %s does not contain uncompressed PCM wave audio"), fn);
return NULL;
}
if (stat (fn, &s) != 0) {
g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_STAT,
- "Could not stat %s", fn);
+ _("Could not stat %s"), fn);
return NULL;
}
@@ -181,8 +196,8 @@
atrack->inputfile = g_strdup (fn);
atrack->pos = -1;
atrack->length = (s.st_size - 44) / PCM_BYTES_PER_SECS;
- atrack->sectors = (s.st_size / 2352);
- if (s.st_size % 2352 > 0)
+ atrack->sectors = (s.st_size / AUDIO_BYTES_PER_SECTORS);
+ if (s.st_size % AUDIO_BYTES_PER_SECTORS > 0)
atrack->sectors++;
return atrack;
@@ -328,20 +343,6 @@
return FALSE;
}
-static gboolean
-free_burning_resources (XfburnTranscoder *trans, XfburnAudioTrack *atrack, GError **error)
-{
- /*
- XfburnTranscoderBasic *basic = XFBURN_TRANSCODER_BASIC (trans);
- XfburnTranscoderBasicPrivate *priv= XFBURN_TRANSCODER_BASIC_GET_PRIVATE (basic);
- */
-
- close (atrack->fd);
- burn_source_free (atrack->src);
-
- return TRUE;
-}
-
/* */
/* public */
/* */
Modified: xfburn/trunk/xfburn/xfburn-transcoder-gst.c
===================================================================
--- xfburn/trunk/xfburn/xfburn-transcoder-gst.c 2008-10-23 12:51:56 UTC (rev 5776)
+++ xfburn/trunk/xfburn/xfburn-transcoder-gst.c 2008-10-23 16:44:06 UTC (rev 5777)
@@ -55,16 +55,24 @@
static void transcoder_interface_init (XfburnTranscoderInterface *iface, gpointer iface_data);
/* internals */
+static void create_pipeline (XfburnTranscoderGst *trans);
+static void delete_pipeline (XfburnTranscoderGst *trans);
+static void recreate_pipeline (XfburnTranscoderGst *trans);
+static const gchar * get_name (XfburnTranscoder *trans);
static XfburnAudioTrack * get_audio_track (XfburnTranscoder *trans, const gchar *fn, GError **error);
-static gboolean has_audio_ext (const gchar *path);
-static gboolean is_valid_wav (const gchar *path);
-static gboolean valid_wav_headers (guchar header[44]);
static struct burn_track * create_burn_track (XfburnTranscoder *trans, XfburnAudioTrack *atrack, GError **error);
-static gboolean needs_swap (char header[44]);
+static gboolean prepare (XfburnTranscoder *trans, GError **error);
+static gboolean transcode_next_track (XfburnTranscoderGst *trans, GError **error);
static gboolean free_burning_resources (XfburnTranscoder *trans, XfburnAudioTrack *atrack, GError **error);
+static gboolean is_initialized (XfburnTranscoder *trans, GError **error);
+
+/* gstreamer support functions */
+static gboolean bus_call (GstBus *bus, GstMessage *msg, gpointer data);
+static void on_pad_added (GstElement *element, GstPad *pad, gboolean last, gpointer data);
+
#define XFBURN_TRANSCODER_GST_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), XFBURN_TYPE_TRANSCODER_GST, XfburnTranscoderGstPrivate))
enum {
@@ -72,9 +80,33 @@
};
typedef struct {
- gboolean dummy;
+ GstElement *pipeline;
+ GstElement *source, *decoder, *conv, *sink;
+
+ gboolean is_transcoding;
+ GCond *gst_cond;
+ GMutex *gst_mutex;
+ gboolean is_audio;
+ gint64 duration;
+
+ GError *error;
+
+ GSList *tracks;
+
} XfburnTranscoderGstPrivate;
+
+typedef struct {
+ int fd_in;
+} XfburnAudioTrackGst;
+
+#define SIGNAL_WAIT_TIMEOUT_MICROS 600000
+
+#define SIGNAL_SEND_ITERATIONS 10
+#define SIGNAL_SEND_TIMEOUT_MICROS 400000
+
+#define XFBURN_AUDIO_TRACK_GET_GST(atrack) ((XfburnAudioTrackGst *) (atrack)->data)
+
/*********************/
/* class declaration */
/*********************/
@@ -135,175 +167,386 @@
static void
xfburn_transcoder_gst_init (XfburnTranscoderGst * obj)
{
- //XfburnTranscoderGstPrivate *priv = XFBURN_TRANSCODER_GST_GET_PRIVATE (obj);
+ XfburnTranscoderGstPrivate *priv = XFBURN_TRANSCODER_GST_GET_PRIVATE (obj);
+
+ create_pipeline (obj);
+
+ /* the condition is used to signal that
+ * gst has returned information */
+ priv->gst_cond = g_cond_new ();
+
+ /* if the mutex is locked, then we're not currently seeking
+ * information from gst */
+ priv->gst_mutex = g_mutex_new ();
+ g_mutex_lock (priv->gst_mutex);
}
static void
xfburn_transcoder_gst_finalize (GObject * object)
{
- //XfburnTranscoderGstPrivate *priv = XFBURN_TRANSCODER_GST_GET_PRIVATE (object);
+ XfburnTranscoderGstPrivate *priv = XFBURN_TRANSCODER_GST_GET_PRIVATE (object);
+ gst_element_set_state (priv->pipeline, GST_STATE_NULL);
+
+ gst_object_unref (GST_OBJECT (priv->pipeline));
+ priv->pipeline = NULL;
+
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
transcoder_interface_init (XfburnTranscoderInterface *iface, gpointer iface_data)
{
+ iface->get_name = get_name;
+ iface->is_initialized = is_initialized;
iface->get_audio_track = get_audio_track;
iface->create_burn_track = create_burn_track;
iface->free_burning_resources = free_burning_resources;
+ iface->prepare = prepare;
}
+
/* */
/* internals */
/* */
-static XfburnAudioTrack *
-get_audio_track (XfburnTranscoder *trans, const gchar *fn, GError **error)
+static void
+create_pipeline (XfburnTranscoderGst *trans)
{
- XfburnAudioTrack *atrack;
- struct stat s;
+ XfburnTranscoderGstPrivate *priv= XFBURN_TRANSCODER_GST_GET_PRIVATE (trans);
- if (!has_audio_ext (fn)) {
- g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_NOT_AUDIO_EXT,
- "File %s does not have a .wav extension", fn);
- return NULL;
+ GstElement *pipeline, *source, *decoder, *conv, *sink;
+ GstBus *bus;
+ GstCaps *caps;
+
+
+ priv->pipeline = pipeline = gst_pipeline_new ("transcoder");
+
+ priv->source = source = gst_element_factory_make ("filesrc", "file-source");
+ priv->decoder = decoder = gst_element_factory_make ("decodebin", "decoder");
+ priv->conv = conv = gst_element_factory_make ("audioconvert", "converter");
+ priv->sink = sink = gst_element_factory_make ("fdsink", "audio-output");
+ //priv->sink = sink = gst_element_factory_make ("fakesink", "audio-output");
+ //DBG ("\npipeline = %p\nsource = %p\ndecoder = %p\nconv = %p\nsink = %p", pipeline, source, decoder, conv, sink);
+
+ if (!pipeline || !source || !decoder || !conv || !sink) {
+ g_set_error (&(priv->error), XFBURN_ERROR, XFBURN_ERROR_GST_CREATION,
+ _("A pipeline element could not be created"));
+ return;
}
- if (!is_valid_wav (fn)) {
- g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_NOT_AUDIO_FORMAT,
- "File %s does not contain uncompressed PCM wave audio", fn);
- return NULL;
- }
- if (stat (fn, &s) != 0) {
- g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_STAT,
- "Could not stat %s", fn);
- return NULL;
+
+ /* we add a message handler */
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+ gst_bus_add_watch (bus, bus_call, trans);
+ gst_object_unref (bus);
+
+ gst_bin_add_many (GST_BIN (pipeline),
+ source, decoder, conv, sink, NULL);
+
+ gst_element_link (source, decoder);
+
+ /* setup caps for raw pcm data */
+ caps = gst_caps_new_simple ("audio/x-raw-int",
+ "rate", G_TYPE_INT, 44100,
+ "channels", G_TYPE_INT, 2,
+ "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
+ "width", G_TYPE_INT, 16,
+ "depth", G_TYPE_INT, 16,
+ "signed", G_TYPE_BOOLEAN, TRUE,
+ NULL);
+
+ if (!gst_element_link_filtered (conv, sink, caps)) {
+ g_set_error (&(priv->error), XFBURN_ERROR, XFBURN_ERROR_GST_CREATION,
+ _("Could not setup filtered gstreamer link"));
+ gst_caps_unref (caps);
+ return;
}
+ gst_caps_unref (caps);
- atrack = g_new0 (XfburnAudioTrack, 1);
- /* FIXME: when do we free inputfile?? */
- atrack->inputfile = g_strdup (fn);
- atrack->pos = -1;
- atrack->length = (s.st_size - 44) / PCM_BYTES_PER_SECS;
- atrack->sectors = (s.st_size / 2352);
- if (s.st_size % 2352 > 0)
- atrack->sectors++;
+ g_signal_connect (decoder, "new-decoded-pad", G_CALLBACK (on_pad_added), conv);
+}
- return atrack;
+static void
+delete_pipeline (XfburnTranscoderGst *trans)
+{
+ XfburnTranscoderGstPrivate *priv= XFBURN_TRANSCODER_GST_GET_PRIVATE (trans);
+
+ DBG ("Deleting pipeline");
+ if (gst_element_set_state (priv->pipeline, GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE)
+ g_warning ("Failed to change state to null, deleting pipeline anyways");
+
+ /* give gstreamer a chance to do something */
+ g_thread_yield ();
+
+ g_object_unref (priv->pipeline);
+ priv->pipeline = NULL;
}
-static gboolean
-has_audio_ext (const gchar *path)
+static void recreate_pipeline (XfburnTranscoderGst *trans)
{
- int len = strlen (path);
- const gchar *ext = path + len - 3;
+ delete_pipeline (trans);
+ create_pipeline (trans);
+}
- return (strcmp (ext, "wav") == 0);
+static gchar *
+state_to_str (GstState st)
+{
+ switch (st) {
+ case GST_STATE_VOID_PENDING:
+ return "void";
+ case GST_STATE_NULL:
+ return "null";
+ case GST_STATE_READY:
+ return "ready";
+ case GST_STATE_PAUSED:
+ return "paused";
+ case GST_STATE_PLAYING:
+ return "playing";
+ }
+ return "invalid";
}
-static gboolean
-is_valid_wav (const gchar *path)
+static gboolean
+bus_call (GstBus *bus, GstMessage *msg, gpointer data)
{
- int fd;
- guchar header[44];
- gboolean ret;
+ XfburnTranscoderGst *trans = XFBURN_TRANSCODER_GST (data);
+ XfburnTranscoderGstPrivate *priv= XFBURN_TRANSCODER_GST_GET_PRIVATE (trans);
- fd = open (path, 0);
+ GstFormat fmt;
+ guint secs;
+ //gint64 frames;
- if (fd == -1) {
- xfce_warn (_("Could not open %s!"), path);
- return FALSE;
- }
+ switch (GST_MESSAGE_TYPE (msg)) {
- read (fd, header, 44);
+ case GST_MESSAGE_EOS: {
+ GError *error = NULL;
- ret = valid_wav_headers (header);
+ DBG ("End of stream");
- close (fd);
+ if (!transcode_next_track (trans, &error)) {
+ g_warning ("Error while switching track: %s", error->message);
+ g_error_free (error);
+ }
- return ret;
-}
+ break;
+ }
+ case GST_MESSAGE_ERROR: {
+ gchar *debug;
+ GError *error;
+ int i;
-/*
- * Simple check of .wav headers, most info from
- * http://ccrma.stanford.edu/courses/422/projects/WaveFormat/
- *
- * I did not take particular care to make sure this check is complete.
- * As far as I can tell yes, but not much effort was put into verifying it.
- * FIXME: this works on x86, and does not consider endianness!
- */
-static gboolean
-valid_wav_headers (guchar header[44])
-{
- /* check if first 4 bytes are RIFF or RIFX */
- if (header[0] == 'R' && header[1] == 'I' && header[2] == 'F') {
- if (!(header[3] == 'X' || header[3] == 'F')) {
- g_warning ("File not in riff format");
- return FALSE;
+ gst_message_parse_error (msg, &error, &debug);
+ g_free (debug);
+
+ g_warning ("Gstreamer error: %s\n", error->message);
+ g_error_free (error);
+
+ /* if transcoding, quit here, otherwise we're querying about songs */
+ if (priv->is_transcoding) {
+ DBG ("transcoding, not signaling on error");
+ break;
+ }
+
+ priv->is_audio = FALSE;
+ DBG ("Trying to lock mutex (error)");
+ for (i=0; i<SIGNAL_SEND_ITERATIONS; i++) {
+ if (g_mutex_trylock (priv->gst_mutex))
+ break;
+ g_usleep (SIGNAL_SEND_TIMEOUT_MICROS / SIGNAL_SEND_ITERATIONS);
+ if (i==9) {
+ recreate_pipeline (trans);
+ g_warning ("Noone was there to listen to the gstreamer error: %s", error->message);
+ }
+ }
+ g_cond_signal (priv->gst_cond);
+ g_mutex_unlock (priv->gst_mutex);
+ DBG ("Releasing mutex (error)");
+
+ recreate_pipeline (trans);
+ break;
}
- }
+ case GST_MESSAGE_STATE_CHANGED: {
+ GstState state;
+ GstState pending, old_state;
- /* check if bytes 8-11 are WAVE */
- if (!(header[8] == 'W' && header[9] == 'A' && header[10] == 'V' && header[11] == 'E')) {
- g_warning ("RIFF file not in WAVE format");
- return FALSE;
+ //if (GST_MESSAGE_SRC (msg) == GST_OBJECT (priv->sink))
+ //DBG ("RIGHT SOURCE");
+ //DBG ("source is %p", GST_MESSAGE_SRC (msg));
+
+ gst_message_parse_state_changed (msg, &old_state, &state, &pending);
+ if (pending != 0)
+ DBG ("New state is %s, old is %s and pending is %s", state_to_str(state), state_to_str(old_state), state_to_str(pending));
+ else
+ DBG ("New state is %s, old is %s", state_to_str(state), state_to_str(old_state));
+
+ if (state != GST_STATE_PAUSED)
+ break;
+
+ /* FIXME: just a debugging check, remove later */
+ if (priv->is_transcoding) {
+ DBG ("We should not be here while transcoding!");
+ break;
+ }
+
+ if (!g_mutex_trylock (priv->gst_mutex)) {
+ DBG ("Lock held by another thread, not doing anything");
+ return TRUE;
+ } else {
+ DBG ("Locked mutex");
+ }
+
+ fmt = GST_FORMAT_TIME;
+ if (!gst_element_query_duration (priv->pipeline, &fmt, &priv->duration)) {
+ g_mutex_unlock (priv->gst_mutex);
+ DBG ("Could not query stream length!");
+ return TRUE;
+ }
+
+ secs = priv->duration / 1000000000;
+ DBG ("Length is %lldns = %ds = %lld bytes\n", priv->duration, secs, priv->duration * 176400 /1000000000);
+ if (gst_element_set_state (priv->pipeline, GST_STATE_READY) == GST_STATE_CHANGE_FAILURE) {
+ DBG ("Failed to set state!");
+ }
+
+ priv->is_audio = TRUE;
+ g_cond_signal (priv->gst_cond);
+ g_mutex_unlock (priv->gst_mutex);
+ DBG ("Releasing mutex (success)");
+
+ break;
+ }
+ default:
+ DBG ("bus call: %s (%d) ", GST_MESSAGE_TYPE_NAME (msg), GST_MESSAGE_TYPE (msg));
+ break;
}
- /* subchunk starts with 'fmt ' */
- if (!(header[12] == 'f' && header[13] == 'm' && header[14] == 't' && header[15] == ' ')) {
- g_warning ("Could not find format subchunk");
- return FALSE;
+ return TRUE;
+}
+
+static void
+on_pad_added (GstElement *element, GstPad *pad, gboolean last, gpointer data)
+{
+ GstCaps *caps;
+ GstStructure *str;
+ GstPad *audiopad;
+ GstElement *audio = (GstElement *) data;
+
+ // only link once
+ audiopad = gst_element_get_static_pad (audio, "sink");
+ if (GST_PAD_IS_LINKED (audiopad)) {
+ DBG ("pads are already linked!");
+ g_object_unref (audiopad);
+ return;
}
- /* check for PCM format */
- if (header[16] != 16 || header[20] != 1) {
- g_warning ("Not in PCM format");
- return FALSE;
+ DBG ("linking pads");
+
+ // check media type
+ caps = gst_pad_get_caps (pad);
+ str = gst_caps_get_structure (caps, 0);
+ if (!g_strrstr (gst_structure_get_name (str), "audio")) {
+ DBG ("not audio!");
+ /* FIXME: report this as an error? */
+ gst_caps_unref (caps);
+ gst_object_unref (audiopad);
+ return;
}
+ gst_caps_unref (caps);
- /* check for stereo */
- if (header[22] != 2) {
- g_warning ("Not in stereo");
- return FALSE;
+ // link'n'play
+ gst_pad_link (pad, audiopad);
+}
+
+
+static const gchar *
+get_name (XfburnTranscoder *trans)
+{
+ return "gstreamer";
+}
+
+static XfburnAudioTrack *
+get_audio_track (XfburnTranscoder *trans, const gchar *fn, GError **error)
+{
+ XfburnTranscoderGst *tgst = XFBURN_TRANSCODER_GST (trans);
+ XfburnTranscoderGstPrivate *priv= XFBURN_TRANSCODER_GST_GET_PRIVATE (tgst);
+
+ XfburnAudioTrack *atrack;
+ GTimeVal tv;
+ guint size;
+
+ priv->is_audio = FALSE;
+ DBG ("setting filename for gstreamer");
+
+ g_object_set (G_OBJECT (priv->source), "location", fn, NULL);
+ if (gst_element_set_state (priv->pipeline, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
+ g_warning ("Supposedly failed to change gstreamer state, ignoring it.");
+ /*
+ g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_GST_STATE,
+ _("Failed to change state!"));
+ return NULL;
+ */
}
+ DBG ("Waiting for signal");
+ g_get_current_time (&tv);
+ g_time_val_add (&tv, SIGNAL_WAIT_TIMEOUT_MICROS);
+ if (!g_cond_timed_wait (priv->gst_cond, priv->gst_mutex, &tv)) {
+ recreate_pipeline (tgst);
+ g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_GST_TIMEOUT,
+ _("Gstreamer did not like this file (detection timed out)"));
+ return NULL;
+ }
+ DBG ("Got a signal");
- /* check for 44100 Hz sample rate,
- * being lazy here and just compare the bytes to what I know they should be */
- if (!(header[24] == 0x44 && header[25] == 0xAC && header[26] == 0 && header[27] == 0)) {
- g_warning ("Does not have a sample rate of 44100 Hz");
- return FALSE;
+ if (!priv->is_audio) {
+ g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_NOT_AUDIO_FORMAT,
+ _("This is not an audio file"));
+ return NULL;
}
- return TRUE;
+ atrack = g_new0 (XfburnAudioTrack, 1);
+ /* FIXME: when do we free inputfile?? */
+ atrack->inputfile = g_strdup (fn);
+ atrack->pos = -1;
+ atrack->length = priv->duration / 1000000000;
+
+ size = (priv->duration / (float) 1000000000) * (float) PCM_BYTES_PER_SECS;
+ atrack->sectors = size / AUDIO_BYTES_PER_SECTORS;
+ if (size % AUDIO_BYTES_PER_SECTORS > 0)
+ atrack->sectors++;
+ DBG ("Track length = %d secs => size = %u bytes => %d sectors", atrack->length, size, atrack->sectors);
+
+ return atrack;
}
+
static struct burn_track *
create_burn_track (XfburnTranscoder *trans, XfburnAudioTrack *atrack, GError **error)
{
- /*
- XfburnTranscoderGst *basic = XFBURN_TRANSCODER_GST (trans);
- XfburnTranscoderGstPrivate *priv= XFBURN_TRANSCODER_GST_GET_PRIVATE (basic);
- */
+ XfburnTranscoderGst *gst = XFBURN_TRANSCODER_GST (trans);
+ XfburnTranscoderGstPrivate *priv= XFBURN_TRANSCODER_GST_GET_PRIVATE (gst);
- char header[44];
struct burn_track *track;
- atrack->fd = open (atrack->inputfile, 0);
- if (atrack->fd == -1) {
- g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_COULD_NOT_OPEN_FILE,
- _("Could not open %s: %s"), atrack->inputfile, g_strerror (errno));
+ XfburnAudioTrackGst *gtrack;
+ int pipe_fd[2];
+
+ if (pipe (pipe_fd) != 0) {
+ g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_PIPE, g_strerror (errno));
return NULL;
}
- /* advance the fd so that libburn skips the header,
- * also allows us to check for byte swapping */
- read (atrack->fd, header, 44);
+ atrack->fd = pipe_fd[0];
+ DBG ("track %d fd = %d", atrack->pos, atrack->fd);
+
atrack->src = burn_fd_source_new (atrack->fd, -1 , 0);
if (atrack->src == NULL) {
g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_BURN_SOURCE,
_("Could not create burn_source from %s!"), atrack->inputfile);
+ XFBURN_AUDIO_TRACK_DELETE_DATA (atrack);
+ atrack->fd = -1;
+ close (pipe_fd[0]); close (pipe_fd[1]);
return NULL;
}
@@ -312,40 +555,116 @@
if (burn_track_set_source (track, atrack->src) != BURN_SOURCE_OK) {
g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_BURN_SOURCE,
_("Could not add source to track %s!"), atrack->inputfile);
+ XFBURN_AUDIO_TRACK_DELETE_DATA (atrack);
+ burn_source_free (atrack->src);
+ atrack->fd = -1;
+ close (pipe_fd[0]); close (pipe_fd[1]);
return NULL;
}
- if (needs_swap (header))
- burn_track_set_byte_swap (track, TRUE);
+ gtrack = g_new0 (XfburnAudioTrackGst, 1);
+ gtrack->fd_in = pipe_fd[1];
+ atrack->data = (gpointer) gtrack;
+
+ //burn_track_set_byte_swap (track, TRUE);
+
burn_track_define_data (track, 0, 0, 1, BURN_AUDIO);
+ priv->tracks = g_slist_prepend (priv->tracks, atrack);
+
return track;
}
static gboolean
-needs_swap (char header[44])
+prepare (XfburnTranscoder *trans, GError **error)
{
- if (header[0] == 'R' && header[1] == 'I' && header[2] == 'F' && header[3] == 'X')
+ XfburnTranscoderGst *gst = XFBURN_TRANSCODER_GST (trans);
+ XfburnTranscoderGstPrivate *priv= XFBURN_TRANSCODER_GST_GET_PRIVATE (gst);
+ gboolean ret;
+
+ priv->tracks = g_slist_reverse (priv->tracks);
+ priv->is_transcoding = TRUE;
+
+ ret = transcode_next_track (gst, error);
+
+ /* give gstreamer a tiny bit of time.
+ * FIXME: what's a good time here?
+ * or rather, we should wait for the state change... */
+ g_usleep (100000);
+
+ return ret;
+}
+
+static gboolean
+transcode_next_track (XfburnTranscoderGst *trans, GError **error)
+{
+ XfburnTranscoderGst *basic = XFBURN_TRANSCODER_GST (trans);
+ XfburnTranscoderGstPrivate *priv= XFBURN_TRANSCODER_GST_GET_PRIVATE (basic);
+ XfburnAudioTrack *atrack;
+ XfburnAudioTrackGst *gtrack;
+
+ if (!priv->tracks)
+ /* we're done transcoding, so just return without error */
return TRUE;
- else
+
+ atrack = (XfburnAudioTrack *) priv->tracks->data;
+ gtrack = XFBURN_AUDIO_TRACK_GET_GST (atrack);
+
+ g_object_set (G_OBJECT (priv->source), "location", atrack->inputfile, NULL);
+ g_object_set (G_OBJECT (priv->sink), "fd", gtrack->fd_in, NULL);
+ DBG ("now transcoding %s -> %d", atrack->inputfile, gtrack->fd_in);
+
+ if (gst_element_set_state (priv->pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
+ g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_GST_STATE,
+ _("Failed to change songs while transcoding"));
return FALSE;
+ }
+
+ priv->tracks = g_slist_next (priv->tracks);
+
+ return TRUE;
}
static gboolean
free_burning_resources (XfburnTranscoder *trans, XfburnAudioTrack *atrack, GError **error)
{
- /*
XfburnTranscoderGst *basic = XFBURN_TRANSCODER_GST (trans);
XfburnTranscoderGstPrivate *priv= XFBURN_TRANSCODER_GST_GET_PRIVATE (basic);
- */
- close (atrack->fd);
- burn_source_free (atrack->src);
+ XfburnAudioTrackGst *gtrack = XFBURN_AUDIO_TRACK_GET_GST (atrack);
+
+ /* is there a better place to put this?
+ * It doesn't hurt that we execute it for every track, but it's just not so pretty */
+ DBG ("Done transcoding!");
+ priv->is_transcoding = FALSE;
+ close (gtrack->fd_in);
+
+ g_free (gtrack);
+ atrack->data = NULL;
+
return TRUE;
}
+
+static gboolean
+is_initialized (XfburnTranscoder *trans, GError **error)
+{
+ XfburnTranscoderGst *basic = XFBURN_TRANSCODER_GST (trans);
+ XfburnTranscoderGstPrivate *priv= XFBURN_TRANSCODER_GST_GET_PRIVATE (basic);
+
+ if (priv->error) {
+ *error = priv->error;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+
+
/* */
/* public */
/* */
Modified: xfburn/trunk/xfburn/xfburn-transcoder.c
===================================================================
--- xfburn/trunk/xfburn/xfburn-transcoder.c 2008-10-23 12:51:56 UTC (rev 5776)
+++ xfburn/trunk/xfburn/xfburn-transcoder.c 2008-10-23 16:44:06 UTC (rev 5777)
@@ -21,6 +21,9 @@
#include <config.h>
#endif /* !HAVE_CONFIG_H */
+#include <unistd.h>
+#include <libxfce4util/libxfce4util.h>
+
#include "xfburn-global.h"
#include "xfburn-transcoder.h"
@@ -91,6 +94,28 @@
/* public */
/* */
+const gchar *
+xfburn_transcoder_get_name (XfburnTranscoder *trans)
+{
+ XfburnTranscoderInterface *iface = XFBURN_TRANSCODER_GET_INTERFACE (trans);
+ if (iface->get_name)
+ return iface->get_name (trans);
+ else
+ return "no";
+}
+
+gboolean
+xfburn_transcoder_is_initialized (XfburnTranscoder *trans, GError **error)
+{
+ XfburnTranscoderInterface *iface = XFBURN_TRANSCODER_GET_INTERFACE (trans);
+ if (iface->is_initialized)
+ return iface->is_initialized (trans, error);
+
+ g_warning ("Falling back to base implementation for xfburn_transcoder_is_initialized, which always says false.");
+ g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_NOT_IMPLEMENTED, _("not implemented"));
+ return FALSE;
+}
+
XfburnAudioTrack *
xfburn_transcoder_get_audio_track (XfburnTranscoder *trans, const gchar *fn, GError **error)
{
@@ -99,7 +124,7 @@
return iface->get_audio_track (trans, fn, error);
g_warning ("Falling back to base implementation for xfburn_transcoder_get_audio_track, which always says false.");
- g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_NOT_IMPLEMENTED, "xfburn_transcoder_get_audio_track is not implemented");
+ g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_NOT_IMPLEMENTED, _("not implemented"));
return NULL;
}
@@ -111,23 +136,53 @@
return iface->create_burn_track (trans, atrack, error);
g_warning ("Falling back to empty base implementation for xfburn_transcoder_create_burn_track.");
- g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_NOT_IMPLEMENTED, "xfburn_transcoder_create_burn_track is not implemented");
+ g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_NOT_IMPLEMENTED, _("not implemented"));
return NULL;
}
gboolean
+xfburn_transcoder_prepare (XfburnTranscoder *trans, GError **error)
+{
+ XfburnTranscoderInterface *iface = XFBURN_TRANSCODER_GET_INTERFACE (trans);
+
+ if (iface->prepare)
+ return iface->prepare (trans, error);
+
+ /* this function is not required by the interface */
+ return TRUE;
+}
+
+gboolean
xfburn_transcoder_free_burning_resources (XfburnTranscoder *trans, XfburnAudioTrack *atrack, GError **error)
{
XfburnTranscoderInterface *iface = XFBURN_TRANSCODER_GET_INTERFACE (trans);
+
+ /* these are part of XfburnAudioTrack, and will be present for all implementations */
+ close (atrack->fd);
+ burn_source_free (atrack->src);
+
+ /* allow for additional resource deallocation */
if (iface->free_burning_resources)
return iface->free_burning_resources (trans, atrack, error);
- g_warning ("Falling back to empty base implementation for xfburn_transcoder_free_burning_resources.");
- g_set_error (error, XFBURN_ERROR, XFBURN_ERROR_NOT_IMPLEMENTED, "xfburn_transcoder_free_burning_resources is not implemented");
- return FALSE;
+ /* this function is not required by the interface */
+ return TRUE;
}
+void
+xfburn_transcoder_free_track (XfburnTranscoder *trans, XfburnAudioTrack *atrack)
+{
+ //XfburnTranscoderInterface *iface = XFBURN_TRANSCODER_GET_INTERFACE (trans);
+
+ g_free (atrack->inputfile);
+
+ if (atrack->artist)
+ g_free (atrack->artist);
+ if (atrack->title)
+ g_free (atrack->title);
+}
+
void
xfburn_transcoder_set_global (XfburnTranscoder *trans)
{
Modified: xfburn/trunk/xfburn/xfburn-transcoder.h
===================================================================
--- xfburn/trunk/xfburn/xfburn-transcoder.h 2008-10-23 12:51:56 UTC (rev 5776)
+++ xfburn/trunk/xfburn/xfburn-transcoder.h 2008-10-23 16:44:06 UTC (rev 5777)
@@ -32,14 +32,13 @@
#define XFBURN_TYPE_TRANSCODER (xfburn_transcoder_get_type ())
#define XFBURN_TRANSCODER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), XFBURN_TYPE_TRANSCODER, XfburnTranscoder))
-//#define XFBURN_TRANSCODER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), XFBURN_TYPE_TRANSCODER, XfburnTranscoderClass))
#define XFBURN_IS_TRANSCODER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), XFBURN_TYPE_TRANSCODER))
//#define XFBURN_IS_TRANSCODER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), XFBURN_TYPE_TRANSCODER))
#define XFBURN_TRANSCODER_GET_INTERFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), XFBURN_TYPE_TRANSCODER, XfburnTranscoderInterface))
typedef struct
{
- const gchar *inputfile;
+ gchar *inputfile;
gint pos;
gchar *artist;
gchar *title;
@@ -50,17 +49,27 @@
int sectors;
int fd;
struct burn_source *src;
+
+ gpointer data; /* implementations will add extra data here */
} XfburnAudioTrack;
+#define XFBURN_AUDIO_TRACK_DELETE_DATA(atrack) { g_free (atrack->data); atrack->data = NULL; }
+
typedef struct {} XfburnTranscoder; /* dummy struct */
typedef struct
{
GTypeInterface parent;
+ /* required functions */
+ const gchar * (*get_name) (XfburnTranscoder *trans);
+ gboolean (*is_initialized) (XfburnTranscoder *trans, GError **error);
XfburnAudioTrack * (*get_audio_track) (XfburnTranscoder *trans, const gchar *fn, GError **error);
struct burn_track * (*create_burn_track) (XfburnTranscoder *trans, XfburnAudioTrack *atrack, GError **error);
+
+ /* optional functions */
gboolean (*free_burning_resources) (XfburnTranscoder *trans, XfburnAudioTrack *atrack, GError **error);
+ gboolean (*prepare) (XfburnTranscoder *trans, GError **error);
} XfburnTranscoderInterface;
@@ -69,10 +78,18 @@
void xfburn_transcoder_set_global (XfburnTranscoder *trans);
XfburnTranscoder *xfburn_transcoder_get_global ();
+const gchar *xfburn_transcoder_get_name (XfburnTranscoder *trans);
+gboolean xfburn_transcoder_is_initialized (XfburnTranscoder *trans, GError **error);
XfburnAudioTrack * xfburn_transcoder_get_audio_track (XfburnTranscoder *trans, const gchar *fn, GError **error);
struct burn_track *xfburn_transcoder_create_burn_track (XfburnTranscoder *trans, XfburnAudioTrack *atrack, GError **error);
+
+/* optional functions */
gboolean xfburn_transcoder_free_burning_resources (XfburnTranscoder *trans, XfburnAudioTrack *atrack, GError **error);
+gboolean xfburn_transcoder_prepare (XfburnTranscoder *trans, GError **error);
+/* defined purely by the interface */
+void xfburn_transcoder_free_track (XfburnTranscoder *trans, XfburnAudioTrack *atrack);
+
G_END_DECLS
#endif /* __XFBURN_TRANSCODER_H__ */
More information about the Goodies-commits
mailing list