From fa86bf26df04e5396d6b3fe3009992024e4f2fd8 Mon Sep 17 00:00:00 2001 From: Anton Belka Date: Thu, 26 Jul 2012 16:19:57 +0300 Subject: [PATCH] flacenc: add TOC support Add TOC as embedded cuesheets in flac files. https://bugzilla.gnome.org/show_bug.cgi?id=54089 --- ext/flac/gstflacenc.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++--- ext/flac/gstflacenc.h | 1 + 2 files changed, 120 insertions(+), 6 deletions(-) diff --git a/ext/flac/gstflacenc.c b/ext/flac/gstflacenc.c index e4e3022..a7cb2191 100644 --- a/ext/flac/gstflacenc.c +++ b/ext/flac/gstflacenc.c @@ -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; diff --git a/ext/flac/gstflacenc.h b/ext/flac/gstflacenc.h index 0a4e2b4..690b098 100644 --- a/ext/flac/gstflacenc.h +++ b/ext/flac/gstflacenc.h @@ -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 */ -- 2.7.4