#include <gst/audio/audio.h>
#include <gst/riff/riff-media.h>
+#include <gst/base/gstbytewriter.h>
GST_DEBUG_CATEGORY_STATIC (wavenc_debug);
#define GST_CAT_DEFAULT wavenc_debug
#define gst_wavenc_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstWavEnc, gst_wavenc, GST_TYPE_ELEMENT,
+ G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL)
G_IMPLEMENT_INTERFACE (GST_TYPE_TOC_SETTER, NULL)
);
#define WAV_HEADER_LEN 44
static GstBuffer *
-gst_wavenc_create_header_buf (GstWavEnc * wavenc, guint audio_data_size)
+gst_wavenc_create_header_buf (GstWavEnc * wavenc)
{
struct wave_header wave;
GstBuffer *buf;
header = map.data;
memset (header, 0, WAV_HEADER_LEN);
- wave.common.wChannels = wavenc->channels;
- wave.common.wBitsPerSample = wavenc->width;
- wave.common.dwSamplesPerSec = wavenc->rate;
-
- /* Fill out our wav-header with some information */
memcpy (wave.riff.id, "RIFF", 4);
- wave.riff.len = audio_data_size + WAV_HEADER_LEN - 8;
+ wave.riff.len =
+ wavenc->meta_length + wavenc->audio_length + WAV_HEADER_LEN - 8;
memcpy (wave.riff.wav_id, "WAVE", 4);
memcpy (wave.format.id, "fmt ", 4);
wave.format.len = 16;
+ wave.common.wChannels = wavenc->channels;
+ wave.common.wBitsPerSample = wavenc->width;
+ wave.common.dwSamplesPerSec = wavenc->rate;
wave.common.wFormatTag = wavenc->format;
wave.common.wBlockAlign = (wavenc->width / 8) * wave.common.wChannels;
wave.common.dwAvgBytesPerSec =
wave.common.wBlockAlign * wave.common.dwSamplesPerSec;
memcpy (wave.data.id, "data", 4);
- wave.data.len = audio_data_size;
+ wave.data.len = wavenc->audio_length;
memcpy (header, (char *) wave.riff.id, 4);
GST_WRITE_UINT32_LE (header + 4, wave.riff.len);
}
static GstFlowReturn
-gst_wavenc_push_header (GstWavEnc * wavenc, guint audio_data_size)
+gst_wavenc_push_header (GstWavEnc * wavenc)
{
GstFlowReturn ret;
GstBuffer *outbuf;
gst_segment_init (&segment, GST_FORMAT_BYTES);
gst_pad_push_event (wavenc->srcpad, gst_event_new_segment (&segment));
- GST_DEBUG_OBJECT (wavenc, "writing header with datasize=%u", audio_data_size);
+ GST_DEBUG_OBJECT (wavenc, "writing header, meta_size=%u, audio_size=%u",
+ wavenc->meta_length, wavenc->audio_length);
- outbuf = gst_wavenc_create_header_buf (wavenc, audio_data_size);
+ outbuf = gst_wavenc_create_header_buf (wavenc);
GST_BUFFER_OFFSET (outbuf) = 0;
ret = gst_pad_push (wavenc->srcpad, outbuf);
return FALSE;
}
-#if 0
-static struct _maps
-{
- const guint32 id;
- const gchar *name;
-} maps[] = {
- {
- GST_RIFF_INFO_IARL, "Location"}, {
- GST_RIFF_INFO_IART, "Artist"}, {
- GST_RIFF_INFO_ICMS, "Commissioner"}, {
- GST_RIFF_INFO_ICMT, "Comment"}, {
- GST_RIFF_INFO_ICOP, "Copyright"}, {
- GST_RIFF_INFO_ICRD, "Creation Date"}, {
- GST_RIFF_INFO_IENG, "Engineer"}, {
- GST_RIFF_INFO_IGNR, "Genre"}, {
- GST_RIFF_INFO_IKEY, "Keywords"}, {
- GST_RIFF_INFO_INAM, "Title"}, {
- GST_RIFF_INFO_IPRD, "Product"}, {
- GST_RIFF_INFO_ISBJ, "Subject"}, {
- GST_RIFF_INFO_ISFT, "Software"}, {
- GST_RIFF_INFO_ITCH, "Technician"}
-};
-
-static guint32
-get_id_from_name (const char *name)
-{
- int i;
-
- for (i = 0; i < G_N_ELEMENTS (maps); i++) {
- if (strcasecmp (maps[i].name, name) == 0) {
- return maps[i].id;
- }
- }
-
- return 0;
-}
-
static void
-write_metadata (GstWavEnc * wavenc)
+gst_wavparse_tags_foreach (const GstTagList * tags, const gchar * tag,
+ gpointer data)
{
- GString *info_str;
- GList *props;
- int total = 4;
- gboolean need_to_write = FALSE;
-
- info_str = g_string_new ("LIST INFO");
-
- for (props = wavenc->metadata->properties->properties; props;
- props = props->next) {
- GstPropsEntry *entry = props->data;
- const char *name;
- guint32 id;
-
- name = gst_props_entry_get_name (entry);
- id = get_id_from_name (name);
- if (id != 0) {
- const char *text;
- char *tmp;
- int len, req, i;
-
- need_to_write = TRUE; /* We've got at least one entry */
-
- gst_props_entry_get_string (entry, &text);
- len = strlen (text) + 1; /* The length in the file includes the \0 */
-
- tmp = g_strdup_printf ("%" GST_FOURCC_FORMAT "%d%s", GST_FOURCC_ARGS (id),
- GUINT32_TO_LE (len), text);
- g_string_append (info_str, tmp);
- g_free (tmp);
-
- /* Check that we end on an even boundary */
- req = ((len + 8) + 1) & ~1;
- for (i = 0; i < req - len; i++) {
- g_string_append_printf (info_str, "%c", 0);
+ const struct
+ {
+ guint32 fcc;
+ const gchar *tag;
+ } rifftags[] = {
+ {
+ GST_RIFF_INFO_IARL, GST_TAG_LOCATION}, {
+ GST_RIFF_INFO_IART, GST_TAG_ARTIST}, {
+ GST_RIFF_INFO_ICMT, GST_TAG_COMMENT}, {
+ GST_RIFF_INFO_ICOP, GST_TAG_COPYRIGHT}, {
+ GST_RIFF_INFO_ICRD, GST_TAG_DATE}, {
+ GST_RIFF_INFO_IGNR, GST_TAG_GENRE}, {
+ GST_RIFF_INFO_IKEY, GST_TAG_KEYWORDS}, {
+ GST_RIFF_INFO_INAM, GST_TAG_TITLE}, {
+ GST_RIFF_INFO_IPRD, GST_TAG_ALBUM}, {
+ GST_RIFF_INFO_ISBJ, GST_TAG_ALBUM_ARTIST}, {
+ GST_RIFF_INFO_ISFT, GST_TAG_ENCODER}, {
+ GST_RIFF_INFO_ISRC, GST_TAG_ISRC}, {
+ 0, NULL}
+ };
+ gint n;
+ gchar *str = NULL;
+ GstByteWriter *bw = data;
+ for (n = 0; rifftags[n].fcc != 0; n++) {
+ if (!strcmp (rifftags[n].tag, tag)) {
+ if (rifftags[n].fcc == GST_RIFF_INFO_ICRD) {
+ GDate *date;
+ /* special case for the date tag */
+ if (gst_tag_list_get_date (tags, tag, &date)) {
+ str =
+ g_strdup_printf ("%04d:%02d:%02d", g_date_get_year (date),
+ g_date_get_month (date), g_date_get_day (date));
+ g_date_free (date);
+ }
+ } else {
+ gst_tag_list_get_string (tags, tag, &str);
+ }
+ if (str) {
+ gst_byte_writer_put_uint32_le (bw, rifftags[n].fcc);
+ gst_byte_writer_put_uint32_le (bw, GST_ROUND_UP_2 (strlen (str)));
+ gst_byte_writer_put_string (bw, str);
+ g_free (str);
+ str = NULL;
+ break;
}
-
- total += req;
}
}
- if (need_to_write) {
- GstBuffer *buf;
-
- /* Now we've got all the strings together, we can write our length in */
- info_str->str[4] = GUINT32_TO_LE (total);
-
- buf = gst_buffer_new ();
- gst_buffer_set_data (buf, info_str->str, info_str->len);
-
- gst_pad_push (wavenc->srcpad, GST_DATA (buf));
- g_string_free (info_str, FALSE);
- }
-}
-
-static void
-write_cues (GstWavEnc * wavenc)
-{
- GString *cue_string, *point_string;
- GstBuffer *buf;
- GList *cue_list, *c;
- int num_cues, total = 4;
-
- if (gst_props_get (wavenc->metadata->properties,
- "cues", &cue_list, NULL) == FALSE) {
- /* No cues, move along please, nothing to see here */
- return;
- }
-
- /* Space for 'cue ', chunk size and number of cuepoints */
- cue_string = g_string_new ("cue ");
-#define CUEPOINT_SIZE 24
- point_string = g_string_sized_new (CUEPOINT_SIZE);
-
- for (c = cue_list, num_cues = 0; c; c = c->next, num_cues++) {
- GstCaps *cue_caps = c->data;
- guint32 pos;
-
- gst_props_get (cue_caps->properties, "position", &pos, NULL);
-
- point_string->str[0] = GUINT32_TO_LE (num_cues + 1);
- point_string->str[4] = GUINT32_TO_LE (0);
- /* Fixme: There is probably a macro for this */
- point_string->str[8] = 'd';
- point_string->str[9] = 'a';
- point_string->str[10] = 't';
- point_string->str[11] = 'a';
- point_string->str[12] = GUINT32_TO_LE (0);
- point_string->str[16] = GUINT32_TO_LE (0);
- point_string->str[20] = GUINT32_TO_LE (pos);
-
- total += CUEPOINT_SIZE;
- }
-
- /* Set the length and chunk size */
- cue_string->str[4] = GUINT32_TO_LE (total);
- cue_string->str[8] = GUINT32_TO_LE (num_cues);
- /* Stick the cue points on the end */
- g_string_append (cue_string, point_string->str);
- g_string_free (point_string, TRUE);
-
- buf = gst_buffer_new ();
- gst_buffer_set_data (buf, cue_string->str, cue_string->len);
-
- gst_pad_push (wavenc->srcpad, GST_DATA (buf));
- g_string_free (cue_string, FALSE);
}
-static void
-write_labels (GstWavEnc * wavenc)
+static GstFlowReturn
+gst_wavenc_write_tags (GstWavEnc * wavenc)
{
+ const GstTagList *user_tags;
+ GstTagList *tags;
+ guint size;
GstBuffer *buf;
- GString *info_str;
- int total = 4;
- GList *caps;
-
- info_str = g_string_new ("LIST adtl");
- if (gst_props_get (wavenc->metadata->properties, "ltxts", &caps, NULL)) {
- GList *p;
- int i;
-
- for (p = caps, i = 1; p; p = p->next, i++) {
- GstCaps *ltxt_caps = p->data;
- GString *ltxt;
- char *label = NULL;
- int len, req, j;
-
- gst_props_get (ltxt_caps->properties, "name", &label, NULL);
- len = strlen (label);
-
-#define LTXT_SIZE 28
- ltxt = g_string_new ("ltxt ");
- ltxt->str[8] = GUINT32_TO_LE (i); /* Identifier */
- ltxt->str[12] = GUINT32_TO_LE (0); /* Sample Length */
- ltxt->str[16] = GUINT32_TO_LE (0); /* FIXME: Don't save the purpose yet */
- ltxt->str[20] = GUINT16_TO_LE (0); /* Country */
- ltxt->str[22] = GUINT16_TO_LE (0); /* Language */
- ltxt->str[24] = GUINT16_TO_LE (0); /* Dialect */
- ltxt->str[26] = GUINT16_TO_LE (0); /* Code Page */
- g_string_append (ltxt, label);
- g_free (label);
-
- len += LTXT_SIZE;
-
- ltxt->str[4] = GUINT32_TO_LE (len);
-
- /* Check that we end on an even boundary */
- req = ((len + 8) + 1) & ~1;
- for (j = 0; j < req - len; j++) {
- g_string_append_printf (ltxt, "%c", 0);
- }
+ GstByteWriter bw;
- total += req;
+ g_return_val_if_fail (wavenc != NULL, GST_FLOW_OK);
- g_string_append (info_str, ltxt->str);
- g_string_free (ltxt, TRUE);
- }
+ user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (wavenc));
+ if ((!wavenc->tags) && (!user_tags)) {
+ GST_DEBUG_OBJECT (wavenc, "have no tags");
+ return GST_FLOW_OK;
}
+ tags =
+ gst_tag_list_merge (user_tags, wavenc->tags,
+ gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (wavenc)));
- if (gst_props_get (wavenc->metadata->properties, "labels", &caps, NULL)) {
- GList *p;
- int i;
-
- for (p = caps, i = 1; p; p = p->next, i++) {
- GstCaps *labl_caps = p->data;
- GString *labl;
- char *label = NULL;
- int len, req, j;
+ GST_DEBUG_OBJECT (wavenc, "writing tags");
- gst_props_get (labl_caps->properties, "name", &label, NULL);
- len = strlen (label);
+ gst_byte_writer_init_with_size (&bw, 1024, FALSE);
-#define LABL_SIZE 4
- labl = g_string_new ("labl ");
- labl->str[8] = GUINT32_TO_LE (i);
- g_string_append (labl, label);
- g_free (label);
+ /* add LIST INFO chunk */
+ gst_byte_writer_put_data (&bw, (const guint8 *) "LIST", 4);
+ gst_byte_writer_put_uint32_le (&bw, 0);
+ gst_byte_writer_put_data (&bw, (const guint8 *) "INFO", 4);
- len += LABL_SIZE;
+ /* add tags */
+ gst_tag_list_foreach (tags, gst_wavparse_tags_foreach, &bw);
- labl->str[4] = GUINT32_TO_LE (len);
+ /* sets real size of LIST INFO chunk */
+ size = gst_byte_writer_get_pos (&bw);
+ gst_byte_writer_set_pos (&bw, 4);
+ gst_byte_writer_put_uint32_le (&bw, size - 8);
- /* Check our size */
- req = ((len + 8) + 1) & ~1;
- for (j = 0; j < req - len; j++) {
- g_string_append_printf (labl, "%c", 0);
- }
-
- total += req;
+ gst_tag_list_unref (tags);
- g_string_append (info_str, labl->str);
- g_string_free (labl, TRUE);
- }
- }
-
- if (gst_props_get (wavenc->metadata->properties, "notes", &caps, NULL)) {
- GList *p;
- int i;
-
- for (p = caps, i = 1; p; p = p->next, i++) {
- GstCaps *note_caps = p->data;
- GString *note;
- char *label = NULL;
- int len, req, j;
-
- gst_props_get (note_caps->properties, "name", &label, NULL);
- len = strlen (label);
-
-#define NOTE_SIZE 4
- note = g_string_new ("note ");
- note->str[8] = GUINT32_TO_LE (i);
- g_string_append (note, label);
- g_free (label);
-
- len += NOTE_SIZE;
-
- note->str[4] = GUINT32_TO_LE (len);
-
- /* Size check */
- req = ((len + 8) + 1) & ~1;
- for (j = 0; j < req - len; j++) {
- g_string_append_printf (note, "%c", 0);
- }
-
- total += req;
-
- g_string_append (info_str, note->str);
- g_string_free (note, TRUE);
- }
- }
-
- info_str->str[4] = GUINT32_TO_LE (total);
-
- buf = gst_buffer_new ();
- gst_buffer_set_data (buf, info_str->str, info_str->len);
-
- gst_pad_push (wavenc->srcpad, GST_DATA (buf));
- g_string_free (info_str, FALSE);
+ buf = gst_byte_writer_reset_and_get_buffer (&bw);
+ wavenc->meta_length += gst_buffer_get_size (buf);
+ return gst_pad_push (wavenc->srcpad, buf);
}
-#endif
static gboolean
gst_wavenc_is_cue_id_unique (guint32 id, GList * list)
return TRUE;
}
-static gboolean
+static GstFlowReturn
gst_wavenc_write_toc (GstWavEnc * wavenc)
{
GList *list;
guint8 *data;
guint32 ncues, size, cues_size, labls_size, notes_size;
+ if (!wavenc->toc) {
+ GST_DEBUG_OBJECT (wavenc, "have no toc, checking toc_setter");
+ wavenc->toc = gst_toc_setter_get_toc (GST_TOC_SETTER (wavenc));
+ }
+ if (!wavenc->toc) {
+ GST_WARNING_OBJECT (wavenc, "have no toc");
+ return GST_FLOW_OK;
+ }
+
+ toc = gst_toc_ref (wavenc->toc);
size = 0;
cues_size = 0;
labls_size = 0;
notes_size = 0;
- if (wavenc->toc) {
- toc = gst_toc_ref (wavenc->toc);
- } else {
- GST_WARNING_OBJECT (wavenc, "TOC not found");
- return FALSE;
- }
-
/* check if the TOC entries is valid */
list = gst_toc_get_entries (toc);
entry = list->data;
g_list_free_full (wavenc->notes, g_free);
gst_buffer_unmap (buf, &map);
+ wavenc->meta_length += gst_buffer_get_size (buf);
- gst_pad_push (wavenc->srcpad, buf);
-
- return TRUE;
+ return gst_pad_push (wavenc->srcpad, buf);
}
static gboolean
{
gboolean res = TRUE;
GstWavEnc *wavenc;
+ GstTagList *tags;
GstToc *toc;
wavenc = GST_WAVENC (parent);
gst_event_unref (event);
break;
}
- case GST_EVENT_EOS:{
+ case GST_EVENT_EOS:
+ {
+ GstFlowReturn flow;
GST_DEBUG_OBJECT (wavenc, "got EOS");
- if (!wavenc->toc) {
- GST_DEBUG_OBJECT (wavenc, "have no toc, checking toc_setter");
- wavenc->toc = gst_toc_setter_get_toc (GST_TOC_SETTER (wavenc));
- }
- if (wavenc->toc) {
- GST_DEBUG_OBJECT (wavenc, "have toc");
- gst_wavenc_write_toc (wavenc);
+
+ flow = gst_wavenc_write_toc (wavenc);
+ if (flow != GST_FLOW_OK) {
+ GST_WARNING_OBJECT (wavenc, "error pushing toc: %s",
+ gst_flow_get_name (flow));
}
-#if 0
- /* Write our metadata if we have any */
- if (wavenc->metadata) {
- write_metadata (wavenc);
- write_cues (wavenc);
- write_labels (wavenc);
+ flow = gst_wavenc_write_tags (wavenc);
+ if (flow != GST_FLOW_OK) {
+ GST_WARNING_OBJECT (wavenc, "error pushing tags: %s",
+ gst_flow_get_name (flow));
}
-#endif
+
/* write header with correct length values */
- gst_wavenc_push_header (wavenc, wavenc->length);
+ gst_wavenc_push_header (wavenc);
/* we're done with this file */
wavenc->finished_properly = TRUE;
}
res = gst_pad_event_default (pad, parent, event);
break;
+ case GST_EVENT_TAG:
+ gst_event_parse_tag (event, &tags);
+ if (tags) {
+ if (wavenc->tags != tags) {
+ if (wavenc->tags)
+ gst_tag_list_unref (wavenc->tags);
+ wavenc->tags = tags;
+ } else {
+ gst_toc_unref (tags);
+ }
+ }
+ res = gst_pad_event_default (pad, parent, event);
+ break;
default:
res = gst_pad_event_default (pad, parent, event);
break;
g_return_val_if_fail (wavenc->channels > 0, GST_FLOW_FLUSHING);
- if (!wavenc->sent_header) {
- /* use bogus size initially, we'll write the real
- * header when we get EOS and know the exact length */
- flow = gst_wavenc_push_header (wavenc, 0x7FFF0000);
-
+ if (G_UNLIKELY (!wavenc->sent_header)) {
/* starting a file, means we have to finish it properly */
wavenc->finished_properly = FALSE;
- if (flow != GST_FLOW_OK)
+ /* use bogus size initially, we'll write the real
+ * header when we get EOS and know the exact length */
+ flow = gst_wavenc_push_header (wavenc);
+ if (flow != GST_FLOW_OK) {
+ GST_WARNING_OBJECT (wavenc, "error pushing header: %s",
+ gst_flow_get_name (flow));
return flow;
-
+ }
GST_DEBUG_OBJECT (wavenc, "wrote dummy header");
wavenc->sent_header = TRUE;
}
buf = gst_buffer_make_writable (buf);
- GST_BUFFER_OFFSET (buf) = WAV_HEADER_LEN + wavenc->length;
+ GST_BUFFER_OFFSET (buf) = WAV_HEADER_LEN + wavenc->audio_length;
GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET_NONE;
- wavenc->length += gst_buffer_get_size (buf);
+ wavenc->audio_length += gst_buffer_get_size (buf);
flow = gst_pad_push (wavenc->srcpad, buf);
wavenc->channels = 0;
wavenc->width = 0;
wavenc->rate = 0;
- wavenc->length = 0;
+ wavenc->audio_length = 0;
+ wavenc->meta_length = 0;
wavenc->sent_header = FALSE;
/* its true because we haven't writen anything */
wavenc->finished_properly = TRUE;
}
break;
case GST_STATE_CHANGE_READY_TO_NULL:
+ GST_DEBUG_OBJECT (wavenc, "tags: %p", wavenc->tags);
+ if (wavenc->tags) {
+ gst_tag_list_unref (wavenc->tags);
+ wavenc->tags = NULL;
+ }
GST_DEBUG_OBJECT (wavenc, "toc: %p", wavenc->toc);
if (wavenc->toc) {
gst_toc_unref (wavenc->toc);
wavenc->toc = NULL;
}
+ gst_tag_setter_reset_tags (GST_TAG_SETTER (wavenc));
gst_toc_setter_reset (GST_TOC_SETTER (wavenc));
break;
default: