FLAC updates, implement sample accurate seeking, add some convert/query functions...
authorWim Taymans <wim.taymans@gmail.com>
Fri, 7 Jun 2002 20:09:05 +0000 (20:09 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Fri, 7 Jun 2002 20:09:05 +0000 (20:09 +0000)
Original commit message from CVS:
FLAC updates, implement sample accurate seeking, add some convert/query
functions, make the encoder write a header

ext/flac/gstflac.c
ext/flac/gstflacdec.c
ext/flac/gstflacdec.h
ext/flac/gstflacenc.c
ext/flac/gstflacenc.h

index c7bd381..6faa035 100644 (file)
@@ -84,6 +84,12 @@ plugin_init (GModule *module, GstPlugin *plugin)
   GstTypeFactory *type;
   GstCaps *raw_caps, *flac_caps;
 
+  /* this filter needs the bytestream package */
+  if (!gst_library_load ("gstbytestream")) {
+    gst_info ("vorbis:: could not load support library: 'gstbytestream'\n");
+    return FALSE;
+  }
+
   gst_plugin_set_longname (plugin, "The FLAC Lossless compressor Codec");
 
   /* create an elementfactory for the flacenc element */
index a6a07e4..d8c4184 100644 (file)
@@ -50,20 +50,39 @@ static void                 gst_flacdec_class_init          (FlacDecClass *klass);
 static void            gst_flacdec_init                (FlacDec *flacdec);
 
 static void            gst_flacdec_loop                (GstElement *element);
-
-static void            gst_flacdec_metadata_callback   (const FLAC__StreamDecoder *decoder, 
+static GstElementStateReturn
+                       gst_flacdec_change_state        (GstElement *element);
+static gboolean        gst_flacdec_convert_src         (GstPad *pad, GstFormat src_format, gint64 src_value,
+                                                        GstFormat *dest_format, gint64 *dest_value);
+static gboolean                gst_flacdec_src_query           (GstPad *pad, GstPadQueryType type,
+                                                        GstFormat *format, gint64 *value);
+static gboolean        gst_flacdec_src_event           (GstPad *pad, GstEvent *event);
+
+static FLAC__SeekableStreamDecoderReadStatus   
+                       gst_flacdec_read                (const FLAC__SeekableStreamDecoder *decoder, 
+                                                        FLAC__byte buffer[], unsigned *bytes, void *client_data);
+static FLAC__SeekableStreamDecoderSeekStatus   
+                       gst_flacdec_seek                (const FLAC__SeekableStreamDecoder *decoder, 
+                                                        FLAC__uint64 position, void *client_data);
+static FLAC__SeekableStreamDecoderTellStatus   
+                       gst_flacdec_tell                (const FLAC__SeekableStreamDecoder *decoder, 
+                                                        FLAC__uint64 *position, void *client_data);
+static FLAC__SeekableStreamDecoderLengthStatus         
+                       gst_flacdec_length              (const FLAC__SeekableStreamDecoder *decoder, 
+                                                        FLAC__uint64 *length, void *client_data);
+static FLAC__bool      gst_flacdec_eof                 (const FLAC__SeekableStreamDecoder *decoder, 
+                                                        void *client_data);
+static FLAC__StreamDecoderWriteStatus  
+                       gst_flacdec_write               (const FLAC__SeekableStreamDecoder *decoder, 
+                                                        const FLAC__Frame *frame, const FLAC__int32 *buffer[], 
+                                                        void *client_data);
+static void            gst_flacdec_metadata_callback   (const FLAC__SeekableStreamDecoder *decoder, 
                                                         const FLAC__StreamMetaData *metadata, 
                                                         void *client_data);
-static void            gst_flacdec_error_callback      (const FLAC__StreamDecoder *decoder, 
+static void            gst_flacdec_error_callback      (const FLAC__SeekableStreamDecoder *decoder, 
                                                         FLAC__StreamDecoderErrorStatus status, 
                                                         void *client_data);
 
-static FLAC__StreamDecoderReadStatus   gst_flacdec_read        (const FLAC__StreamDecoder *decoder, 
-                                                                FLAC__byte buffer[], unsigned *bytes, void *client_data);
-static FLAC__StreamDecoderWriteStatus  gst_flacdec_write       (const FLAC__StreamDecoder *decoder, 
-                                                                const FLAC__Frame *frame, const FLAC__int32 *buffer[], 
-                                                                void *client_data);
-
 static GstElementClass *parent_class = NULL;
 /*static guint gst_flacdec_signals[LAST_SIGNAL] = { 0 }; */
 
@@ -96,6 +115,8 @@ gst_flacdec_class_init (FlacDecClass *klass)
   gstelement_class = (GstElementClass*)klass;
 
   parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
+
+  gstelement_class->change_state = gst_flacdec_change_state;
 }
 
 static void 
@@ -103,76 +124,184 @@ gst_flacdec_init (FlacDec *flacdec)
 {
   flacdec->sinkpad = gst_pad_new_from_template (dec_sink_template, "sink");
   gst_element_add_pad (GST_ELEMENT (flacdec), flacdec->sinkpad);
+  gst_pad_set_convert_function (flacdec->sinkpad, NULL);
 
   gst_element_set_loop_function (GST_ELEMENT (flacdec), gst_flacdec_loop);
   flacdec->srcpad = gst_pad_new_from_template (dec_src_template, "src");
   gst_element_add_pad (GST_ELEMENT (flacdec), flacdec->srcpad);
+  gst_pad_set_query_function (flacdec->srcpad, gst_flacdec_src_query);
+  gst_pad_set_convert_function (flacdec->srcpad, gst_flacdec_convert_src);
+  gst_pad_set_event_function (flacdec->srcpad, gst_flacdec_src_event);
+
+  flacdec->decoder = FLAC__seekable_stream_decoder_new ();
+  flacdec->total_samples = 0;
+  flacdec->init = TRUE;
+  flacdec->eos = FALSE;
+  flacdec->seek_pending = FALSE;
+
+  FLAC__seekable_stream_decoder_set_read_callback (flacdec->decoder, gst_flacdec_read);
+  FLAC__seekable_stream_decoder_set_seek_callback (flacdec->decoder, gst_flacdec_seek);
+  FLAC__seekable_stream_decoder_set_tell_callback (flacdec->decoder, gst_flacdec_tell);
+  FLAC__seekable_stream_decoder_set_length_callback (flacdec->decoder, gst_flacdec_length);
+  FLAC__seekable_stream_decoder_set_eof_callback (flacdec->decoder, gst_flacdec_eof);
+  FLAC__seekable_stream_decoder_set_write_callback (flacdec->decoder, gst_flacdec_write);
+  FLAC__seekable_stream_decoder_set_metadata_callback (flacdec->decoder, gst_flacdec_metadata_callback);
+  FLAC__seekable_stream_decoder_set_error_callback (flacdec->decoder, gst_flacdec_error_callback);
+  FLAC__seekable_stream_decoder_set_client_data (flacdec->decoder, flacdec);
+}
 
-  flacdec->decoder = FLAC__stream_decoder_new ();
-  flacdec->offset_left = 0;
-  flacdec->data_left = NULL;
+static void 
+gst_flacdec_metadata_callback (const FLAC__SeekableStreamDecoder *decoder,
+                              const FLAC__StreamMetaData *metadata, void *client_data)
+{
+  FlacDec *flacdec;
 
-  FLAC__stream_decoder_set_read_callback (flacdec->decoder, gst_flacdec_read);
-  FLAC__stream_decoder_set_write_callback (flacdec->decoder, gst_flacdec_write);
-  FLAC__stream_decoder_set_metadata_callback (flacdec->decoder, gst_flacdec_metadata_callback);
-  FLAC__stream_decoder_set_error_callback (flacdec->decoder, gst_flacdec_error_callback);
-  FLAC__stream_decoder_set_client_data (flacdec->decoder, flacdec);
+  flacdec = GST_FLACDEC (client_data);
 
-  FLAC__stream_decoder_init (flacdec->decoder);
+  flacdec->stream_samples = metadata->data.stream_info.total_samples;
 }
 
 static void 
-gst_flacdec_metadata_callback (const FLAC__StreamDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data)
+gst_flacdec_error_callback (const FLAC__SeekableStreamDecoder *decoder, 
+                           FLAC__StreamDecoderErrorStatus status, void *client_data)
 {
+  FlacDec *flacdec;
+  gchar *error;
+
+  flacdec = GST_FLACDEC (client_data);
+
+  switch (status) {
+    case FLAC__STREAM_DECODER_ERROR_LOST_SYNC:
+      error = "lost sync";
+      break;
+    case FLAC__STREAM_DECODER_ERROR_BAD_HEADER:
+      error = "bad header";
+      break;
+    case FLAC__STREAM_DECODER_ERROR_FRAME_CRC_MISMATCH:
+      error = "CRC mismatch";
+      break;
+    default:
+      error = "unkown error";
+      break;
+  }
+
+  GST_DEBUG (0, error);
+                                 
+  gst_element_error (GST_ELEMENT (flacdec), error);
 }
 
-static void 
-gst_flacdec_error_callback (const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
+static FLAC__SeekableStreamDecoderSeekStatus   
+gst_flacdec_seek (const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 position, void *client_data)
 {
+  FlacDec *flacdec;
+
+  flacdec = GST_FLACDEC (client_data);
+
+  GST_DEBUG (0, "seek %lld\n", position);
+  if (!gst_bytestream_seek (flacdec->bs, position, GST_SEEK_METHOD_SET)) {
+    return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR;
+  }
+  return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK;
 }
 
-static FLAC__StreamDecoderReadStatus
-gst_flacdec_read (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data)
+static FLAC__SeekableStreamDecoderTellStatus   
+gst_flacdec_tell (const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *position, void *client_data)
 {
   FlacDec *flacdec;
-  GstBuffer *inbuf = NULL;
-  gint insize;
-  guchar *indata;
 
   flacdec = GST_FLACDEC (client_data);
 
-  if (flacdec->data_left == NULL) {
-    inbuf = gst_pad_pull (flacdec->sinkpad);
-    insize = GST_BUFFER_SIZE (inbuf);
-    indata = GST_BUFFER_DATA (inbuf);
-  }
-  else {
-    inbuf = flacdec->data_left;
-    insize = GST_BUFFER_SIZE (inbuf) - flacdec->offset_left;
-    indata = GST_BUFFER_DATA (inbuf) + flacdec->offset_left;
-  }
+  *position = gst_bytestream_tell (flacdec->bs);
+  if (*position == -1)
+    return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR;
 
-  if (*bytes < insize) {
-    /* we have more than we can handle */
-    flacdec->data_left = inbuf;
-    flacdec->offset_left += *bytes;
-    inbuf = NULL;
-  }
-  else {
-    flacdec->data_left = NULL;
-    flacdec->offset_left = 0;
-    *bytes = insize;
+  GST_DEBUG (0, "tell %lld\n", *position);
+
+  return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK;
+}
+
+static FLAC__SeekableStreamDecoderLengthStatus         
+gst_flacdec_length (const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *length, void *client_data)
+{
+  FlacDec *flacdec;
+
+  flacdec = GST_FLACDEC (client_data);
+
+  *length = gst_bytestream_length (flacdec->bs);
+  if (*length == -1)
+    return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR;
+
+  GST_DEBUG (0, "length %lld\n", *length);
+
+  return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK;
+}
+
+static FLAC__bool
+gst_flacdec_eof (const FLAC__SeekableStreamDecoder *decoder, void *client_data)
+{
+  FlacDec *flacdec;
+
+  flacdec = GST_FLACDEC (client_data);
+  GST_DEBUG (0, "eof %d\n", flacdec->eos);
+
+  return flacdec->eos;
+}
+
+static FLAC__SeekableStreamDecoderReadStatus
+gst_flacdec_read (const FLAC__SeekableStreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data)
+{
+  FlacDec *flacdec;
+  gint insize = 0;
+  guint8 *indata;
+
+  flacdec = GST_FLACDEC (client_data);
+
+  //g_print ("read %u\n", *bytes);
+  
+  while (insize == 0) {
+    insize = gst_bytestream_peek_bytes (flacdec->bs, &indata, *bytes);
+    if (insize < *bytes) {
+      GstEvent *event;
+      guint32 avail;
+                           
+      gst_bytestream_get_status (flacdec->bs, &avail, &event);
+
+      switch (GST_EVENT_TYPE (event)) {
+        case GST_EVENT_EOS:
+          GST_DEBUG (0, "eos");
+          flacdec->eos = TRUE; 
+          if (avail == 0) {
+            return 0;
+          }
+          break;
+        case GST_EVENT_DISCONTINUOUS:
+          GST_DEBUG (0, "discont");
+
+         /* we are not yet sending the discont, we'll do that in the next write operation */
+         flacdec->need_discont = TRUE;
+         gst_event_free (event);
+         break;
+        default:
+         gst_pad_event_default (flacdec->sinkpad, event);
+          break;
+      }
+      if (avail > 0)
+        insize = gst_bytestream_peek_bytes (flacdec->bs, &indata, avail);
+      else
+        insize = 0;
+    }
   }
-  memcpy (buffer, indata, *bytes);
 
-  if (inbuf) 
-    gst_buffer_unref (inbuf);
+  memcpy (buffer, indata, insize);
+  *bytes = insize;
+  gst_bytestream_flush_fast (flacdec->bs, insize);
 
-  return FLAC__STREAM_DECODER_READ_CONTINUE;
+  return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
 }
 
 static FLAC__StreamDecoderWriteStatus
-gst_flacdec_write (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *buffer[], void *client_data)
+gst_flacdec_write (const FLAC__SeekableStreamDecoder *decoder, const FLAC__Frame *frame, 
+                  const FLAC__int32 *buffer[], void *client_data)
 {
   FlacDec *flacdec;
   GstBuffer *outbuf;
@@ -183,6 +312,32 @@ gst_flacdec_write (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame,
 
   flacdec = GST_FLACDEC (client_data);
 
+  if (flacdec->need_discont) {
+    gint64 time = 0, bytes = 0;
+    GstFormat format;
+    GstEvent *discont;
+
+    flacdec->need_discont = FALSE;
+
+    if (flacdec->seek_pending) {
+      flacdec->total_samples = flacdec->seek_value;
+    }
+
+    GST_DEBUG (0, "send discont");
+
+    format = GST_FORMAT_TIME;
+    gst_pad_convert (flacdec->srcpad, GST_FORMAT_UNITS, flacdec->total_samples,
+                 &format, &time);
+    format = GST_FORMAT_BYTES;
+    gst_pad_convert (flacdec->srcpad, GST_FORMAT_UNITS, flacdec->total_samples,
+                 &format, &bytes);
+    discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, time,
+                                        GST_FORMAT_BYTES, bytes,
+                                        GST_FORMAT_UNITS, flacdec->total_samples, NULL);
+         
+    gst_pad_push (flacdec->srcpad, GST_BUFFER (discont));
+  }
+  
   if (!GST_PAD_CAPS (flacdec->srcpad)) {
     gst_pad_try_set_caps (flacdec->srcpad,
                    GST_CAPS_NEW (
@@ -197,11 +352,16 @@ gst_flacdec_write (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame,
                          "rate",       GST_PROPS_INT (frame->header.sample_rate),
                          "channels",   GST_PROPS_INT (channels)
                    ));
+
+    flacdec->depth = depth;
+    flacdec->channels = channels;
+    flacdec->frequency = frame->header.sample_rate;
   }
 
   outbuf = gst_buffer_new ();
   GST_BUFFER_SIZE (outbuf) = samples * channels * ((depth+7)>>3);
   GST_BUFFER_DATA (outbuf) = g_malloc (GST_BUFFER_SIZE (outbuf));
+  GST_BUFFER_TIMESTAMP (outbuf) = flacdec->total_samples * GST_SECOND / frame->header.sample_rate;
 
   if (depth == 8) {
     guint8 *outbuffer = (guint8 *)GST_BUFFER_DATA (outbuf);
@@ -226,6 +386,8 @@ gst_flacdec_write (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame,
     return FLAC__STREAM_DECODER_WRITE_ABORT;
   }
 
+  flacdec->total_samples += samples;
+
   gst_pad_push (flacdec->srcpad, outbuf);
 
   return FLAC__STREAM_DECODER_WRITE_CONTINUE;
@@ -235,13 +397,196 @@ static void
 gst_flacdec_loop (GstElement *element) 
 {
   FlacDec *flacdec;
+  gboolean res;
 
   flacdec = GST_FLACDEC (element);
-  
-  if (FLAC__stream_decoder_get_state (flacdec->decoder) == FLAC__STREAM_DECODER_SEARCH_FOR_METADATA) {
-    FLAC__stream_decoder_process_metadata (flacdec->decoder);
+
+  if (flacdec->init) {
+    FLAC__seekable_stream_decoder_init (flacdec->decoder);
+    FLAC__seekable_stream_decoder_process_metadata (flacdec->decoder);
+    flacdec->init = FALSE;
+  }
+
+  if (flacdec->seek_pending) {
+    GST_DEBUG (GST_CAT_EVENT, "perform seek to sample %lld\n", flacdec->seek_value);
+
+    if (FLAC__seekable_stream_decoder_seek_absolute(flacdec->decoder, flacdec->seek_value)) {
+      flacdec->total_samples = flacdec->seek_value;
+      GST_DEBUG (GST_CAT_EVENT, "seek done\n");
+    }
+    else {
+      GST_DEBUG (GST_CAT_EVENT, "seek failed\n");
+    }
+    flacdec->seek_pending = FALSE;
+  }
+
+  res = FLAC__seekable_stream_decoder_process_one_frame (flacdec->decoder);
+  if (FLAC__seekable_stream_decoder_get_state (flacdec->decoder) == FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM) {
+    GstEvent *event;
+
+    FLAC__seekable_stream_decoder_finish(flacdec->decoder);
+    flacdec->init = TRUE;
+
+    event = gst_event_new(GST_EVENT_EOS);
+    gst_pad_push (flacdec->srcpad, GST_BUFFER (event));
+    gst_element_set_eos (element);
+  }
+}
+
+static gboolean
+gst_flacdec_convert_src (GstPad *pad, GstFormat src_format, gint64 src_value,
+                        GstFormat *dest_format, gint64 *dest_value)
+{
+  gboolean res = TRUE;
+  FlacDec *flacdec = GST_FLACDEC (gst_pad_get_parent (pad));
+  guint scale = 1;
+  gint bytes_per_sample;
+
+  bytes_per_sample = flacdec->channels * ((flacdec->depth+7)>>3);
+
+  switch (src_format) {
+    case GST_FORMAT_BYTES:
+      switch (*dest_format) {
+        case GST_FORMAT_UNITS:
+          if (bytes_per_sample == 0)
+            return FALSE;
+          *dest_value = src_value / bytes_per_sample;
+          break;
+        case GST_FORMAT_DEFAULT:
+          *dest_format = GST_FORMAT_TIME;
+        case GST_FORMAT_TIME:
+       {
+          gint byterate = bytes_per_sample * flacdec->frequency;
+
+          if (byterate == 0)
+            return FALSE;
+          *dest_value = src_value * GST_SECOND / byterate;
+          break;
+       }
+        default:
+          res = FALSE;
+      }
+      break;
+    case GST_FORMAT_UNITS:
+      switch (*dest_format) {
+        case GST_FORMAT_BYTES:
+         *dest_value = src_value * bytes_per_sample;
+          break;
+        case GST_FORMAT_DEFAULT:
+          *dest_format = GST_FORMAT_TIME;
+        case GST_FORMAT_TIME:
+         if (flacdec->frequency == 0)
+           return FALSE;
+          *dest_value = src_value * GST_SECOND / flacdec->frequency;
+          break;
+        default:
+          res = FALSE;
+      }
+      break;
+    case GST_FORMAT_TIME:
+      switch (*dest_format) {
+        case GST_FORMAT_DEFAULT:
+          *dest_format = GST_FORMAT_BYTES;
+        case GST_FORMAT_BYTES:
+          scale = bytes_per_sample;
+        case GST_FORMAT_UNITS:
+         *dest_value = src_value * scale * flacdec->frequency / GST_SECOND;
+          break;
+        default:
+          res = FALSE;
+      }
+      break;
+    default:
+      res = FALSE;
+  }
+  return res;
+}
+
+static gboolean
+gst_flacdec_src_query (GstPad *pad, GstPadQueryType type,
+                      GstFormat *format, gint64 *value)
+{
+  gboolean res = TRUE;
+  FlacDec *flacdec = GST_FLACDEC (gst_pad_get_parent (pad));
+
+  switch (type) {
+    case GST_PAD_QUERY_TOTAL:
+    {
+      guint64 samples;
+
+      if (flacdec->stream_samples == 0)
+        samples = flacdec->total_samples;
+      else
+        samples = flacdec->stream_samples;
+
+      gst_pad_convert (flacdec->srcpad, GST_FORMAT_UNITS, samples, format, value);
+      break;
+    }
+    case GST_PAD_QUERY_POSITION:
+      gst_pad_convert (flacdec->srcpad, GST_FORMAT_UNITS, flacdec->total_samples, format, value);
+      break;
+    default:
+      res = FALSE;
+      break;
+  }
+
+  return res;
+}
+         
+static gboolean
+gst_flacdec_src_event (GstPad *pad, GstEvent *event)
+{ 
+  gboolean res = TRUE;
+  FlacDec *flacdec = GST_FLACDEC (gst_pad_get_parent (pad));
+  GstFormat format;
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_SEEK:
+      format = GST_FORMAT_UNITS;
+
+      if (gst_pad_convert (flacdec->srcpad, GST_EVENT_SEEK_FORMAT (event), GST_EVENT_SEEK_OFFSET (event), 
+                          &format, &flacdec->seek_value))
+        flacdec->seek_pending = TRUE;
+      else
+       res = FALSE;
+      break;
+    default:
+      res = FALSE;
+      break;
+  }
+  return res;
+}
+
+static GstElementStateReturn
+gst_flacdec_change_state (GstElement *element)
+{
+  FlacDec *flacdec = GST_FLACDEC (element);
+
+  switch (GST_STATE_TRANSITION (element)) {
+    case GST_STATE_NULL_TO_READY:
+    case GST_STATE_READY_TO_PAUSED:
+      flacdec->init = TRUE;
+      flacdec->bs = gst_bytestream_new (flacdec->sinkpad);
+      flacdec->seek_pending = FALSE;
+      flacdec->total_samples = 0;
+      flacdec->init = TRUE;
+      flacdec->eos = FALSE;
+      break;
+    case GST_STATE_PAUSED_TO_PLAYING:
+      flacdec->eos = FALSE;
+    case GST_STATE_PLAYING_TO_PAUSED:
+      break;
+    case GST_STATE_PAUSED_TO_READY:
+      gst_bytestream_destroy (flacdec->bs);
+      break;
+    case GST_STATE_READY_TO_NULL:
+    default:
+      break;
   }
 
-  FLAC__stream_decoder_process_one_frame (flacdec->decoder);
+  if (GST_ELEMENT_CLASS (parent_class)->change_state)
+    return GST_ELEMENT_CLASS (parent_class)->change_state (element);
+
+  return GST_STATE_SUCCESS;
 }
 
index 6faf171..6ba447f 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <config.h>
 #include <gst/gst.h>
+#include <gst/bytestream/bytestream.h>
 
 #include <FLAC/all.h>
 
@@ -42,17 +43,25 @@ typedef struct _FlacDec FlacDec;
 typedef struct _FlacDecClass FlacDecClass;
 
 struct _FlacDec {
-  GstElement element;
+  GstElement    element;
 
-  GstPad *sinkpad,*srcpad;
+  GstPad       *sinkpad,*srcpad;
+  GstByteStream *bs;
 
-  FLAC__StreamDecoder *decoder;
+  FLAC__SeekableStreamDecoder *decoder;
+  gint          channels;
+  gint          depth;
+  gint          frequency;
 
-  gint offset_left;
-  GstBuffer *data_left;
+  gboolean      need_discont;
+  gboolean      seek_pending;
+  gint64        seek_value;
 
-  gboolean eos;
-  guint state;
+  gboolean      init;
+  guint64       total_samples;
+  guint64       stream_samples;
+
+  gboolean      eos;
 };
 
 struct _FlacDecClass {
index 0119f95..a9b9af9 100644 (file)
@@ -132,10 +132,15 @@ gst_flacenc_init (FlacEnc *flacenc)
   flacenc->srcpad = gst_pad_new_from_template (enc_src_template, "src");
   gst_element_add_pad(GST_ELEMENT(flacenc),flacenc->srcpad);
 
+  flacenc->first = TRUE;
+  flacenc->first_buf = NULL;
   flacenc->encoder = FLAC__stream_encoder_new();
+
   FLAC__stream_encoder_set_write_callback (flacenc->encoder, gst_flacenc_write_callback);
   FLAC__stream_encoder_set_metadata_callback (flacenc->encoder, gst_flacenc_metadata_callback);
   FLAC__stream_encoder_set_client_data (flacenc->encoder, flacenc);
+
+  GST_FLAG_SET (flacenc, GST_ELEMENT_EVENT_AWARE);
 }
 
 static FLAC__StreamEncoderWriteStatus 
@@ -153,6 +158,12 @@ gst_flacenc_write_callback (const FLAC__StreamEncoder *encoder, const FLAC__byte
 
   memcpy (GST_BUFFER_DATA (outbuf), buffer, bytes);
 
+  if (flacenc->first) {
+    flacenc->first_buf = outbuf;
+    gst_buffer_ref (outbuf);
+    flacenc->first = FALSE;
+  }
+
   gst_pad_push (flacenc->srcpad, outbuf);
 
   return FLAC__STREAM_ENCODER_WRITE_OK;
@@ -161,10 +172,49 @@ gst_flacenc_write_callback (const FLAC__StreamEncoder *encoder, const FLAC__byte
 static void 
 gst_flacenc_metadata_callback (const FLAC__StreamEncoder *encoder, const FLAC__StreamMetaData *metadata, void *client_data)
 {
+  GstEvent *event;
+  FlacEnc *flacenc;
+
+  flacenc = GST_FLACENC (client_data);
+
+  event = gst_event_new_discontinuous (FALSE, GST_FORMAT_BYTES, 0, NULL);
+  gst_pad_push (flacenc->srcpad, GST_BUFFER (event));
+
+  if (flacenc->first_buf) {
+    const FLAC__uint64 samples = metadata->data.stream_info.total_samples;
+    const unsigned min_framesize = metadata->data.stream_info.min_framesize;
+    const unsigned max_framesize = metadata->data.stream_info.max_framesize;
+
+    guint8 *data = GST_BUFFER_DATA (flacenc->first_buf);
+    GstBuffer *outbuf = flacenc->first_buf;
+
+    /* this looks evil but is actually how one is supposed to write
+     * the stream stats according to the FLAC examples */
+
+    memcpy (&data[26], metadata->data.stream_info.md5sum, 16);
+    
+    data[21] = (data[21] & 0xf0) | 
+              (FLAC__byte)((samples >> 32) & 0x0f);
+    data[22] = (FLAC__byte)((samples >> 24) & 0xff);
+    data[23] = (FLAC__byte)((samples >> 16) & 0xff);
+    data[24] = (FLAC__byte)((samples >> 8 ) & 0xff);
+    data[25] = (FLAC__byte)((samples      ) & 0xff);
+
+    data[12] = (FLAC__byte)((min_framesize >> 16) & 0xFF);
+    data[13] = (FLAC__byte)((min_framesize >> 8 ) & 0xFF);
+    data[14] = (FLAC__byte)((min_framesize      ) & 0xFF);
+
+    data[15] = (FLAC__byte)((max_framesize >> 16) & 0xFF);
+    data[16] = (FLAC__byte)((max_framesize >> 8 ) & 0xFF);
+    data[17] = (FLAC__byte)((max_framesize      ) & 0xFF);
+
+    flacenc->first_buf = NULL;
+    gst_pad_push (flacenc->srcpad, outbuf);
+  }
 }
 
 static void
-gst_flacenc_chain (GstPad *pad,GstBuffer *buf)
+gst_flacenc_chain (GstPad *pad, GstBuffer *buf)
 {
   FlacEnc *flacenc;
   gint32 *data[FLAC__MAX_CHANNELS];
@@ -179,6 +229,19 @@ gst_flacenc_chain (GstPad *pad,GstBuffer *buf)
 
   flacenc = GST_FLACENC (gst_pad_get_parent (pad));
 
+  if (GST_IS_EVENT (buf)) {
+    GstEvent *event = GST_EVENT (buf);
+
+    switch (GST_EVENT_TYPE (event)) {
+      case GST_EVENT_EOS:
+       FLAC__stream_encoder_finish(flacenc->encoder);
+      default:
+       gst_pad_event_default (pad, event);
+        break;
+    }
+    return;
+  }
+
   channels = flacenc->channels;
   depth = flacenc->depth;
 
index 1fe5e90..bca59c8 100644 (file)
@@ -41,14 +41,16 @@ typedef struct _FlacEnc FlacEnc;
 typedef struct _FlacEncClass FlacEncClass;
 
 struct _FlacEnc {
-  GstElement element;
+  GstElement    element;
 
   GstPad *sinkpad,*srcpad;
 
-  gboolean eos;
-  gint channels;
-  gint depth;
-  gint sample_rate;
+  gboolean      first;
+  GstBuffer    *first_buf;
+  gboolean      eos;
+  gint                  channels;
+  gint                  depth;
+  gint                  sample_rate;
 
   FLAC__StreamEncoder *encoder;
 };