? ChangeLog
? xing.diff
Index: gstlame.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-ugly/ext/lame/gstlame.c,v
retrieving revision 1.86
diff -u -r1.86 gstlame.c
--- gstlame.c	23 Jan 2006 18:37:16 -0000	1.86
+++ gstlame.c	7 Feb 2006 21:50:56 -0000
@@ -66,6 +66,7 @@
         "channels = (int) [ 1, 2 ]")
     );
 
+
 /********** Define useful types for non-programmatic interfaces **********/
 #define GST_TYPE_LAME_MODE (gst_lame_mode_get_type())
 static GType
@@ -570,6 +571,150 @@
   GST_DEBUG_OBJECT (lame, "done initializing");
 }
 
+/* Xing Header stuff */
+struct _GstXingInfoPriv {
+  guint64 duration;
+  guint64 byte_count;
+  GList *seek_table;
+  gboolean flush;
+};
+
+#define GST_XING_FRAME_FIELD   (1 << 0)
+#define GST_XING_BYTES_FIELD   (1 << 1)
+#define GST_XING_TOC_FIELD     (1 << 2)
+#define GST_XING_QUALITY_FIELD (1 << 3)
+
+static void
+xing_set_flush (GstLame *lame, gboolean flush)
+{
+  if (lame->xing_info == NULL) {
+    return;
+  }
+  lame->xing_info->flush = flush;
+}
+
+static void
+xing_init_header (GstLame *lame)
+{
+  lame->xing_info = g_malloc0 (sizeof (GstXingInfoPriv));
+  xing_set_flush (lame, TRUE);
+  lame->xing_info->duration = GST_CLOCK_TIME_NONE;
+}
+
+static void
+xing_update_data (GstLame *lame, gint bytes, guint64 duration)
+{
+  if (lame->xing_info == NULL) {
+    return;
+  }
+
+  lame->xing_info->duration += duration;
+  lame->xing_info->byte_count +=  bytes;
+}
+
+static GstBuffer *
+xing_get_header (GstLame *lame)
+{
+  guint32 xing_flags;
+  GstBuffer *header;
+  guint32 *data;
+  /* Dummy header that we will stick at the beginning of our frame
+   *
+   * 0xffe => synchronization bits
+   * 0x1b  => 11010b (11b == MPEG1 | 01b == Layer III | 0b == no CRC)
+   * 0x9   => 128kbps
+   * 0x00  => 00b == 44100 Hz | 0b == no padding | 0b == private bit
+   * 0x44  => 0010b 0010b (00b == stereo | 10b == (unused) mode extension)
+   *                      (0b == no copyright bit | 0b == original bit)
+   *                      (00b == no emphasis)
+   *
+   * Such a frame (MPEG1 Layer III) contains 1152 samples, its size is thus:
+   * (1152*(128000/8))/44100 = 417.96
+   * 
+   * There are also 32 bytes (ie 8 32 bits values) to skip after the header 
+   * for such frames
+   */
+  const gchar mp3_header[4] = {0xff, 0xfb, 0x90, 0x44};
+  const int FRAME_SIZE = 418;
+  const int SIDE_INFO_SIZE = 32/sizeof (guint32);
+  
+  header = gst_buffer_new_and_alloc (FRAME_SIZE);
+
+  data = (guint32 *)GST_BUFFER_DATA(header);
+  memset (data, 0, FRAME_SIZE);
+  strncpy ((void *)data, mp3_header, 4);
+  strncpy ((void *)&data[8+1], "Xing", 4);
+
+  xing_flags = 0;
+  if (lame->xing_info->duration != GST_CLOCK_TIME_NONE) {
+    guint number_of_frames;
+    /* The Xing Header contains a NumberOfFrames field, which verifies to:
+     * Duration = NumberOfFrames *SamplesPerFrame/SamplingRate
+     * SamplesPerFrame and SamplingRate are values for the current frame, 
+     * ie 1152 and 44100 in our case.
+     */
+    number_of_frames = (44100*lame->xing_info->duration/GST_SECOND)/1152;
+    data[SIDE_INFO_SIZE+3] = GUINT32_TO_BE (number_of_frames);
+    
+    xing_flags |= GST_XING_FRAME_FIELD;
+  }
+
+  if (lame->xing_info->byte_count != 0) {
+    xing_flags |= GST_XING_BYTES_FIELD;
+    data[SIDE_INFO_SIZE+4] = GUINT32_TO_BE (lame->xing_info->byte_count);
+  }
+
+  /* Un-#ifdef when it's implemented :) lame code in VbrTag.c looks like
+   * it could be stolen
+   */
+#if 0
+  if (lame->xing_info->seek_table != NULL) {
+    GList *it;
+    
+    xing_flags |= GST_XING_TOC_FIELD;
+    for (it = lame->xing_info->seek_table; it != NULL; it = it->next) {
+      /* do something */
+    }    
+  }
+#endif
+
+  data[SIDE_INFO_SIZE+2] = GUINT32_TO_BE (xing_flags);
+
+  gst_util_dump_mem ((guchar *)data, FRAME_SIZE);
+  return header;
+}
+
+static gboolean
+xing_ready_to_flush (GstLame *lame)
+{
+  if (lame->xing_info == NULL) {
+    return FALSE;
+  }
+  if (lame->xingheader == FALSE) {
+    return FALSE;
+  }
+  return lame->xing_info->flush;
+}
+
+static GstFlowReturn
+xing_push_header (GstLame *lame, gboolean seek)
+{
+  GstBuffer *header;
+
+  if (seek != FALSE) {
+    GstEvent *event;
+    event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
+				       0, GST_CLOCK_TIME_NONE, 0);
+    
+    gst_pad_push_event (lame->srcpad, event);
+  }
+
+  header = xing_get_header (lame);
+  xing_set_flush (lame, FALSE);
+
+  return gst_pad_push (lame->srcpad, header);
+}
+
 typedef struct _GstLameTagMatch GstLameTagMatch;
 typedef void (*GstLameTagFunc) (lame_global_flags * gfp, const char *value);
 
@@ -793,6 +938,11 @@
       break;
     case ARG_XINGHEADER:
       lame->xingheader = g_value_get_boolean (value);
+      g_free (lame->xing_info);
+      lame->xing_info = NULL;
+      if (lame->xingheader) {
+	xing_init_header (lame);
+      }
       break;
 #ifdef GSTLAME_PRESET
     case ARG_PRESET:
@@ -934,7 +1084,7 @@
     case GST_EVENT_EOS:
       GST_DEBUG_OBJECT (lame, "handling EOS event");
       /* FIXME, push last data packet */
-
+      xing_push_header (lame, TRUE);
       gst_pad_push_event (lame->srcpad, event);
       break;
     case GST_EVENT_FLUSH_START:
@@ -994,6 +1144,10 @@
 
   if (!lame->setup)
     goto not_setup;
+  
+  if (xing_ready_to_flush (lame)) {
+    return xing_push_header (lame, FALSE);
+  }
 
   num_samples = GST_BUFFER_SIZE (buf) / 2;
 
@@ -1055,6 +1209,7 @@
     result = gst_pad_push (lame->srcpad, outbuf);
 
     lame->last_ts = GST_CLOCK_TIME_NONE;
+    xing_update_data (lame, mp3_size, duration);
   } else {
     g_free (mp3_data);
     result = GST_FLOW_OK;
@@ -1144,7 +1299,6 @@
   CHECK_ERROR (lame_set_allow_diff_short (lame->lgf, lame->allow_diff_short));
   CHECK_ERROR (lame_set_no_short_blocks (lame->lgf, lame->no_short_blocks));
   CHECK_ERROR (lame_set_emphasis (lame->lgf, lame->emphasis));
-  CHECK_ERROR (lame_set_bWriteVbrTag (lame->lgf, lame->xingheader ? 1 : 0));
 #ifdef GSTLAME_PRESET
   if (lame->preset > 0) {
     CHECK_ERROR (lame_set_preset (lame->lgf, lame->preset));
Index: gstlame.h
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-ugly/ext/lame/gstlame.h,v
retrieving revision 1.21
diff -u -r1.21 gstlame.h
--- gstlame.h	23 Jan 2006 18:37:16 -0000	1.21
+++ gstlame.h	7 Feb 2006 21:50:56 -0000
@@ -49,6 +49,7 @@
 
 typedef struct _GstLame GstLame;
 typedef struct _GstLameClass GstLameClass;
+typedef struct _GstXingInfoPriv GstXingInfoPriv;
 
 struct _GstLame {
   GstElement element;
@@ -92,6 +93,7 @@
   gboolean no_short_blocks;
   gboolean emphasis;
   gboolean xingheader;
+  GstXingInfoPriv *xing_info;
   gint preset;
 
   lame_global_flags *lgf;
