flacenc: add TOC support
authorAnton Belka <antonbelka@gmail.com>
Thu, 26 Jul 2012 13:19:57 +0000 (16:19 +0300)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Tue, 7 Aug 2012 16:04:41 +0000 (18:04 +0200)
Add TOC as embedded cuesheets in flac files.

https://bugzilla.gnome.org/show_bug.cgi?id=54089

ext/flac/gstflacenc.c
ext/flac/gstflacenc.h

index e4e3022..a7cb219 100644 (file)
@@ -140,7 +140,9 @@ GST_DEBUG_CATEGORY_STATIC (flacenc_debug);
 
 #define gst_flac_enc_parent_class parent_class
 G_DEFINE_TYPE_WITH_CODE (GstFlacEnc, gst_flac_enc, GST_TYPE_AUDIO_ENCODER,
-    G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL));
+    G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL)
+    G_IMPLEMENT_INTERFACE (GST_TYPE_TOC_SETTER, NULL)
+    );
 
 static gboolean gst_flac_enc_start (GstAudioEncoder * enc);
 static gboolean gst_flac_enc_stop (GstAudioEncoder * enc);
@@ -415,6 +417,7 @@ gst_flac_enc_start (GstAudioEncoder * enc)
   flacenc->offset = 0;
   flacenc->eos = FALSE;
   flacenc->tags = gst_tag_list_new_empty ();
+  flacenc->toc = NULL;
 
   return TRUE;
 }
@@ -427,6 +430,9 @@ gst_flac_enc_stop (GstAudioEncoder * enc)
   GST_DEBUG_OBJECT (enc, "stop");
   gst_tag_list_unref (flacenc->tags);
   flacenc->tags = NULL;
+  if (flacenc->toc)
+    gst_toc_unref (flacenc->toc);
+  flacenc->toc = NULL;
   if (FLAC__stream_encoder_get_state (flacenc->encoder) !=
       FLAC__STREAM_ENCODER_UNINITIALIZED) {
     flacenc->stopped = TRUE;
@@ -441,6 +447,9 @@ gst_flac_enc_stop (GstAudioEncoder * enc)
     if (flacenc->meta[2])
       FLAC__metadata_object_delete (flacenc->meta[2]);
 
+    if (flacenc->meta[3])
+      FLAC__metadata_object_delete (flacenc->meta[3]);
+
     g_free (flacenc->meta);
     flacenc->meta = NULL;
   }
@@ -449,6 +458,7 @@ gst_flac_enc_stop (GstAudioEncoder * enc)
   flacenc->headers = NULL;
 
   gst_tag_setter_reset_tags (GST_TAG_SETTER (enc));
+  gst_toc_setter_reset (GST_TOC_SETTER (enc));
 
   return TRUE;
 }
@@ -481,17 +491,94 @@ add_one_tag (const GstTagList * list, const gchar * tag, gpointer user_data)
   g_list_free (comments);
 }
 
+static gboolean
+add_cuesheet (const GstToc * toc, guint sample_rate,
+    FLAC__StreamMetadata * cuesheet)
+{
+  gint8 track_num = 0;
+  gint64 start, stop;
+  gchar *isrc = NULL;
+  const gchar *is_legal;
+  GList *list;
+  GstTagList *tags;
+  GstTocEntry *entry;
+  FLAC__StreamMetadata_CueSheet *cs;
+  FLAC__StreamMetadata_CueSheet_Track *track;
+
+  cs = &cuesheet->data.cue_sheet;
+  if (!cs)
+    return FALSE;
+
+  /* check if the TOC entries is valid */
+  list = gst_toc_get_entries (toc);
+  while (list) {
+    entry = list->data;
+    if (!gst_toc_entry_is_sequence (entry))
+      return FALSE;
+    list = g_list_next (list);
+  }
+
+  /* add tracks in cuesheet */
+  list = gst_toc_get_entries (toc);
+  while (list) {
+    entry = list->data;
+    gst_toc_entry_get_start_stop_times (entry, &start, &stop);
+    tags = gst_toc_entry_get_tags (entry);
+    if (tags)
+      gst_tag_list_get_string (tags, GST_TAG_ISRC, &isrc);
+    track = FLAC__metadata_object_cuesheet_track_new ();
+    track->offset =
+        (FLAC__uint64) gst_util_uint64_scale_round (start, sample_rate,
+        GST_SECOND);
+    track->number = (FLAC__byte) track_num + 1;
+    if (isrc)
+      strcpy (track->isrc, isrc);
+    if (track->number <= 0)
+      return FALSE;
+    if (!FLAC__metadata_object_cuesheet_insert_track (cuesheet, track_num,
+            track, FALSE))
+      return FALSE;
+    if (!FLAC__metadata_object_cuesheet_track_insert_blank_index (cuesheet,
+            track_num, 0))
+      return FALSE;
+    track_num++;
+    list = g_list_next (list);
+  }
+
+  if (cs->num_tracks <= 0)
+    return FALSE;
+
+  /* add lead-out track in cuesheet */
+  track = FLAC__metadata_object_cuesheet_track_new ();
+  track->offset =
+      (FLAC__uint64) gst_util_uint64_scale_round (stop, sample_rate,
+      GST_SECOND);
+  track->number = 255;
+  if (!FLAC__metadata_object_cuesheet_insert_track (cuesheet, cs->num_tracks,
+          track, FALSE))
+    return FALSE;
+
+  /* check if the cuesheet is valid */
+  if (!FLAC__metadata_object_cuesheet_is_legal (cuesheet, FALSE, &is_legal)) {
+    g_warning ("%s\n", is_legal);
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
 static void
-gst_flac_enc_set_metadata (GstFlacEnc * flacenc, guint64 total_samples)
+gst_flac_enc_set_metadata (GstFlacEnc * flacenc, GstAudioInfo * info,
+    guint64 total_samples)
 {
   const GstTagList *user_tags;
   GstTagList *copy;
   gint entries = 1;
   gint n_images, n_preview_images;
-  GstAudioInfo *info =
-      gst_audio_encoder_get_audio_info (GST_AUDIO_ENCODER (flacenc));
+  FLAC__StreamMetadata *cuesheet;
 
   g_return_if_fail (flacenc != NULL);
+
   user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (flacenc));
   if ((flacenc->tags == NULL) && (user_tags == NULL)) {
     return;
@@ -502,12 +589,26 @@ gst_flac_enc_set_metadata (GstFlacEnc * flacenc, guint64 total_samples)
   n_preview_images = gst_tag_list_get_tag_size (copy, GST_TAG_PREVIEW_IMAGE);
 
   flacenc->meta =
-      g_new0 (FLAC__StreamMetadata *, 3 + n_images + n_preview_images);
+      g_new0 (FLAC__StreamMetadata *, 4 + n_images + n_preview_images);
 
   flacenc->meta[0] =
       FLAC__metadata_object_new (FLAC__METADATA_TYPE_VORBIS_COMMENT);
   gst_tag_list_foreach (copy, add_one_tag, flacenc);
 
+  if (!flacenc->toc)
+    flacenc->toc = gst_toc_setter_get_toc (GST_TOC_SETTER (flacenc));
+
+  if (flacenc->toc) {
+    cuesheet = FLAC__metadata_object_new (FLAC__METADATA_TYPE_CUESHEET);
+    if (add_cuesheet (flacenc->toc, GST_AUDIO_INFO_RATE (info), cuesheet)) {
+      flacenc->meta[entries] = cuesheet;
+      entries++;
+    } else {
+      FLAC__metadata_object_delete (cuesheet);
+      flacenc->meta[entries] = NULL;
+    }
+  }
+
   if (n_images + n_preview_images > 0) {
     GstSample *sample;
     GstBuffer *buffer;
@@ -727,7 +828,7 @@ gst_flac_enc_set_format (GstAudioEncoder * enc, GstAudioInfo * info)
     FLAC__stream_encoder_set_total_samples_estimate (flacenc->encoder,
         MIN (total_samples, G_GUINT64_CONSTANT (0x0FFFFFFFFF)));
 
-  gst_flac_enc_set_metadata (flacenc, total_samples);
+  gst_flac_enc_set_metadata (flacenc, info, total_samples);
 
   /* callbacks clear to go now;
    * write callbacks receives headers during init */
@@ -1094,6 +1195,7 @@ gst_flac_enc_sink_event (GstAudioEncoder * enc, GstEvent * event)
 {
   GstFlacEnc *flacenc;
   GstTagList *taglist;
+  GstToc *toc;
   gboolean ret = FALSE;
 
   flacenc = GST_FLAC_ENC (enc);
@@ -1115,6 +1217,17 @@ gst_flac_enc_sink_event (GstAudioEncoder * enc, GstEvent * event)
       }
       ret = GST_AUDIO_ENCODER_CLASS (parent_class)->sink_event (enc, event);
       break;
+    case GST_EVENT_TOC:
+      gst_event_parse_toc (event, &toc, NULL);
+      if (toc) {
+        if (flacenc->toc != toc) {
+          if (flacenc->toc)
+            gst_toc_unref (flacenc->toc);
+          flacenc->toc = toc;
+        }
+      }
+      ret = GST_AUDIO_ENCODER_CLASS (parent_class)->sink_event (enc, event);
+      break;
     default:
       ret = GST_AUDIO_ENCODER_CLASS (parent_class)->sink_event (enc, event);
       break;
index 0a4e2b4..690b098 100644 (file)
@@ -57,6 +57,7 @@ struct _GstFlacEnc {
   FLAC__StreamMetadata **meta;
 
   GstTagList *     tags;
+  GstToc *         toc;
 
   gboolean         eos;
   /* queue headers until we have them all so we can add streamheaders to caps */