ext/taglib/gsttaglib.*: Fix newsegment event handling a bit. We need to cache the first newsegment event, because we ...

Original commit message from CVS:
* ext/taglib/gsttaglib.cc:
* ext/taglib/gsttaglib.h:
Fix newsegment event handling a bit. We need to
cache the first newsegment event, because we can't
adjust offsets yet when we get it, as we don't
know the size of the tag yet for sure at that point.
Also do some minor cleaning up here and there and add
some debug statements.
This commit is contained in:
Tim-Philipp Müller 2006-03-26 19:56:37 +00:00
parent 022687f7ab
commit 7b3777a4a1
4 changed files with 167 additions and 83 deletions

View file

@ -1,3 +1,14 @@
2006-03-26 Tim-Philipp Müller <tim at centricular dot net>
* ext/taglib/gsttaglib.cc:
* ext/taglib/gsttaglib.h:
Fix newsegment event handling a bit. We need to
cache the first newsegment event, because we can't
adjust offsets yet when we get it, as we don't
know the size of the tag yet for sure at that point.
Also do some minor cleaning up here and there and add
some debug statements.
2006-03-25 Tim-Philipp Müller <tim at centricular dot net> 2006-03-25 Tim-Philipp Müller <tim at centricular dot net>
* ext/taglib/gsttaglib.cc: * ext/taglib/gsttaglib.cc:

2
common

@ -1 +1 @@
Subproject commit 5685efc3f9976d6abe3fec557353fc2053b0e3fb Subproject commit 45cc64e522d61410eb8d1a3e7ef67569851cd77a

View file

@ -61,6 +61,26 @@ using namespace TagLib;
GST_DEBUG_CATEGORY_STATIC (gst_tag_lib_mux_debug); GST_DEBUG_CATEGORY_STATIC (gst_tag_lib_mux_debug);
#define GST_CAT_DEFAULT gst_tag_lib_mux_debug #define GST_CAT_DEFAULT gst_tag_lib_mux_debug
static const GstElementDetails gst_tag_lib_mux_details =
GST_ELEMENT_DETAILS ("TagLib ID3 Muxer",
"Formatter/Metadata",
"Adds an ID3v2 header to the beginning of MP3 files",
"Christophe Fergeau <teuf@gnome.org>");
static GstStaticPadTemplate gst_tag_lib_mux_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/mpeg"));
static GstStaticPadTemplate gst_tag_lib_mux_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/x-id3"));
static void static void
gst_tag_lib_mux_iface_init (GType taglib_type) gst_tag_lib_mux_iface_init (GType taglib_type)
{ {
@ -83,47 +103,29 @@ gst_tag_lib_mux_change_state (GstElement * element, GstStateChange transition);
static GstFlowReturn gst_tag_lib_mux_chain (GstPad * pad, GstBuffer * buffer); static GstFlowReturn gst_tag_lib_mux_chain (GstPad * pad, GstBuffer * buffer);
static gboolean gst_tag_lib_mux_sink_event (GstPad * pad, GstEvent * event); static gboolean gst_tag_lib_mux_sink_event (GstPad * pad, GstEvent * event);
static void static void
gst_tag_lib_mux_finalize (GObject * obj) gst_tag_lib_mux_finalize (GObject * obj)
{ {
GstTagLibMux *taglib = GST_TAGLIB_MUX (obj); GstTagLibMux *taglib = GST_TAGLIB_MUX (obj);
if (taglib->tags) { if (taglib->newsegment_ev) {
gst_tag_list_free (taglib->tags); gst_event_unref (taglib->newsegment_ev);
taglib->tags = NULL; taglib->newsegment_ev = NULL;
} }
if (taglib->event_tags) {
gst_tag_list_free (taglib->event_tags);
taglib->event_tags = NULL;
}
G_OBJECT_CLASS (parent_class)->finalize (obj); G_OBJECT_CLASS (parent_class)->finalize (obj);
} }
static GstStaticPadTemplate gst_tag_lib_mux_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/mpeg"));
static GstStaticPadTemplate gst_tag_lib_mux_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/x-id3"));
static void static void
gst_tag_lib_mux_base_init (gpointer g_class) gst_tag_lib_mux_base_init (gpointer g_class)
{ {
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
static GstElementDetails gst_tag_lib_mux_details = {
"TagLib ID3 Muxer",
"Formatter/Metadata",
"Adds an ID3v2 header to the beginning of MP3 files",
"Christophe Fergeau <teuf@gnome.org>"
};
gst_element_class_add_pad_template (element_class, gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&gst_tag_lib_mux_src_template)); gst_static_pad_template_get (&gst_tag_lib_mux_src_template));
gst_element_class_add_pad_template (element_class, gst_element_class_add_pad_template (element_class,
@ -380,7 +382,6 @@ add_one_tag (const GstTagList * list, const gchar * tag, gpointer user_data)
} }
} }
static GstBuffer * static GstBuffer *
gst_tag_lib_mux_render_tag (GstTagLibMux * taglib) gst_tag_lib_mux_render_tag (GstTagLibMux * taglib)
{ {
@ -388,30 +389,46 @@ gst_tag_lib_mux_render_tag (GstTagLibMux * taglib)
ByteVector rendered_tag; ByteVector rendered_tag;
GstBuffer *buffer; GstBuffer *buffer;
GstTagSetter *tagsetter = GST_TAG_SETTER (taglib); GstTagSetter *tagsetter = GST_TAG_SETTER (taglib);
const GstTagList *tagsetter_tags;
GstTagList *taglist; GstTagList *taglist;
GstEvent *event; GstEvent *event;
if (taglib->tags != NULL) { if (taglib->event_tags != NULL) {
taglist = gst_tag_list_copy (taglib->tags); taglist = gst_tag_list_copy (taglib->event_tags);
} else { } else {
taglist = gst_tag_list_new (); taglist = gst_tag_list_new ();
} }
if (gst_tag_setter_get_tag_list (tagsetter)) { tagsetter_tags = gst_tag_setter_get_tag_list (tagsetter);
gst_tag_list_insert (taglist, if (tagsetter_tags) {
gst_tag_setter_get_tag_list (tagsetter), GstTagMergeMode merge_mode;
gst_tag_setter_get_tag_merge_mode (tagsetter));
merge_mode = gst_tag_setter_get_tag_merge_mode (tagsetter);
GST_LOG_OBJECT (taglib, "merging tags, merge mode = %d", merge_mode);
GST_LOG_OBJECT (taglib, "event tags: %" GST_PTR_FORMAT, taglist);
GST_LOG_OBJECT (taglib, "set tags: %" GST_PTR_FORMAT, tagsetter_tags);
gst_tag_list_insert (taglist, tagsetter_tags, merge_mode);
} }
GST_LOG_OBJECT (taglib, "final tags: %" GST_PTR_FORMAT, taglist);
/* Render the tag */ /* Render the tag */
gst_tag_list_foreach (taglist, add_one_tag, &id3v2tag); gst_tag_list_foreach (taglist, add_one_tag, &id3v2tag);
rendered_tag = id3v2tag.render (); rendered_tag = id3v2tag.render ();
taglib->tag_size = rendered_tag.size (); taglib->tag_size = rendered_tag.size ();
buffer = gst_buffer_new_and_alloc (rendered_tag.size ());
memcpy (GST_BUFFER_DATA (buffer), rendered_tag.data (), rendered_tag.size ()); GST_LOG_OBJECT (taglib, "tag size = %d bytes", taglib->tag_size);
/* Create buffer with tag */
buffer = gst_buffer_new_and_alloc (taglib->tag_size);
memcpy (GST_BUFFER_DATA (buffer), rendered_tag.data (), taglib->tag_size);
gst_buffer_set_caps (buffer, GST_PAD_CAPS (taglib->srcpad)); gst_buffer_set_caps (buffer, GST_PAD_CAPS (taglib->srcpad));
/* gst_util_dump_mem (GST_BUFFER_DATA (buffer), rendered_tag.size()); */
/* Send newsegment event from byte position 0, so the tag really gets
* written to the start of the file, independent of the upstream segment */
gst_pad_push_event (taglib->srcpad,
gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0));
/* Send an event about the new tags to downstream elements */ /* Send an event about the new tags to downstream elements */
/* gst_event_new_tag takes ownership of the list, so no need to unref it */ /* gst_event_new_tag takes ownership of the list, so no need to unref it */
@ -423,6 +440,31 @@ gst_tag_lib_mux_render_tag (GstTagLibMux * taglib)
return buffer; return buffer;
} }
static GstEvent *
gst_tag_lib_mux_adjust_event_offsets (GstTagLibMux * taglib,
const GstEvent * newsegment_event)
{
GstFormat format;
gint64 start, stop, cur;
gst_event_parse_new_segment ((GstEvent *) newsegment_event, NULL, NULL,
&format, &start, &stop, &cur);
g_assert (format == GST_FORMAT_BYTES);
if (start != -1)
start += taglib->tag_size;
if (stop != -1)
stop += taglib->tag_size;
if (cur != -1)
cur += taglib->tag_size;
GST_DEBUG_OBJECT (taglib, "adjusting newsegment event offsets to start=%"
G_GINT64_FORMAT ", stop=%" G_GINT64_FORMAT ", cur=%" G_GINT64_FORMAT
" (delta = +%u)", start, stop, cur, taglib->tag_size);
return gst_event_new_new_segment (TRUE, 1.0, format, start, stop, cur);
}
static GstFlowReturn static GstFlowReturn
gst_tag_lib_mux_chain (GstPad * pad, GstBuffer * buffer) gst_tag_lib_mux_chain (GstPad * pad, GstBuffer * buffer)
@ -435,12 +477,27 @@ gst_tag_lib_mux_chain (GstPad * pad, GstBuffer * buffer)
GST_INFO_OBJECT (taglib, "Adding tags to stream"); GST_INFO_OBJECT (taglib, "Adding tags to stream");
ret = gst_pad_push (taglib->srcpad, gst_tag_lib_mux_render_tag (taglib)); ret = gst_pad_push (taglib->srcpad, gst_tag_lib_mux_render_tag (taglib));
if (ret != GST_FLOW_OK) { if (ret != GST_FLOW_OK) {
GST_DEBUG_OBJECT (taglib, "flow: %s", gst_flow_get_name (ret));
gst_buffer_unref (buffer); gst_buffer_unref (buffer);
return ret; return ret;
} }
/* Now send the cached newsegment event that we got from upstream */
if (taglib->newsegment_ev) {
GST_DEBUG_OBJECT (taglib, "sending cached newsegment event");
gst_pad_push_event (taglib->srcpad,
gst_tag_lib_mux_adjust_event_offsets (taglib, taglib->newsegment_ev));
gst_event_unref (taglib->newsegment_ev);
taglib->newsegment_ev = NULL;
} else {
/* upstream sent no newsegment event or only one in a non-BYTE format */
}
taglib->render_tag = FALSE; taglib->render_tag = FALSE;
} }
buffer = gst_buffer_make_metadata_writable (buffer);
if (GST_BUFFER_OFFSET (buffer) != GST_BUFFER_OFFSET_NONE) { if (GST_BUFFER_OFFSET (buffer) != GST_BUFFER_OFFSET_NONE) {
GST_LOG_OBJECT (taglib, "Adjusting buffer offset from %" G_GINT64_FORMAT GST_LOG_OBJECT (taglib, "Adjusting buffer offset from %" G_GINT64_FORMAT
" to %" G_GINT64_FORMAT, GST_BUFFER_OFFSET (buffer), " to %" G_GINT64_FORMAT, GST_BUFFER_OFFSET (buffer),
@ -462,59 +519,68 @@ gst_tag_lib_mux_sink_event (GstPad * pad, GstEvent * event)
result = FALSE; result = FALSE;
switch (GST_EVENT_TYPE (event)) { switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_TAG: case GST_EVENT_TAG:{
{
GstTagList *tags; GstTagList *tags;
GST_INFO ("Got tag event");
gst_event_parse_tag (event, &tags); gst_event_parse_tag (event, &tags);
if (taglib->tags != NULL) {
/* FIXME: which policy is the best here? PREPEND or something else? */ GST_INFO_OBJECT (taglib, "Got tag event: %" GST_PTR_FORMAT, tags);
gst_tag_list_insert (taglib->tags, tags, GST_TAG_MERGE_PREPEND);
if (taglib->event_tags != NULL) {
gst_tag_list_insert (taglib->event_tags, tags, GST_TAG_MERGE_REPLACE);
} else { } else {
taglib->tags = gst_tag_list_copy (tags); taglib->event_tags = gst_tag_list_copy (tags);
} }
/* We'll push a new tag event in render_tag */
GST_INFO_OBJECT (taglib, "Event tags are now: %" GST_PTR_FORMAT,
taglib->event_tags);
/* just drop the event, we'll push a new tag event in render_tag */
gst_event_unref (event); gst_event_unref (event);
result = TRUE; result = TRUE;
break; break;
} }
case GST_EVENT_NEWSEGMENT: case GST_EVENT_NEWSEGMENT:{
if (taglib->tag_size == 0) { GstFormat fmt;
result = gst_pad_push_event (taglib->srcpad, event);
} else {
gboolean update;
gdouble rate;
GstFormat format;
gint64 value, end_value, base;
gst_event_parse_new_segment (event, &update, &rate, &format, gst_event_parse_new_segment (event, NULL, NULL, &fmt, NULL, NULL, NULL);
&value, &end_value, &base);
if (fmt != GST_FORMAT_BYTES) {
GST_WARNING_OBJECT (taglib, "dropping newsegment event in %s format",
gst_format_get_name (fmt));
gst_event_unref (event); gst_event_unref (event);
if (format == GST_FORMAT_BYTES && gst_pad_is_linked (taglib->srcpad)) {
GstEvent *new_event;
GST_INFO ("Adjusting NEW_SEGMENT event by %d", taglib->tag_size);
value += taglib->tag_size;
if (end_value != -1) {
end_value += taglib->tag_size;
}
new_event = gst_event_new_new_segment (update, rate, format,
value, end_value, base);
result = gst_pad_push_event (taglib->srcpad, new_event);
} else {
result = FALSE;
}
}
break; break;
}
if (taglib->render_tag) {
/* we have not rendered the tag yet, which means that we don't know
* how large it is going to be yet, so we can't adjust the offsets
* here at this point and need to cache the newsegment event for now
* (also, there could be tag events coming after this newsegment event
* and before the first buffer). */
if (taglib->newsegment_ev) {
GST_WARNING_OBJECT (taglib, "discarding old cached newsegment event");
gst_event_unref (taglib->newsegment_ev);
}
GST_LOG_OBJECT (taglib, "caching newsegment event for later");
taglib->newsegment_ev = event;
} else {
GST_DEBUG_OBJECT (taglib, "got newsegment event, adjusting offsets");
gst_pad_push_event (taglib->srcpad,
gst_tag_lib_mux_adjust_event_offsets (taglib, event));
gst_event_unref (event);
}
event = NULL;
result = TRUE;
break;
}
default: default:
result = gst_pad_event_default (pad, event); result = gst_pad_event_default (pad, event);
break; break;
} }
gst_object_unref (GST_OBJECT (taglib));
gst_object_unref (taglib);
return result; return result;
} }
@ -534,14 +600,19 @@ gst_tag_lib_mux_change_state (GstElement * element, GstStateChange transition)
} }
switch (transition) { switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY: case GST_STATE_CHANGE_PAUSED_TO_READY:{
if (taglib->tags) { if (taglib->newsegment_ev) {
gst_tag_list_free (taglib->tags); gst_event_unref (taglib->newsegment_ev);
taglib->tags = NULL; taglib->newsegment_ev = NULL;
}
if (taglib->event_tags) {
gst_tag_list_free (taglib->event_tags);
taglib->event_tags = NULL;
} }
taglib->tag_size = 0; taglib->tag_size = 0;
taglib->render_tag = TRUE; taglib->render_tag = TRUE;
break; break;
}
default: default:
break; break;
} }

View file

@ -30,11 +30,13 @@ typedef struct _GstTagLibMuxPriv GstTagLibMuxPriv;
typedef struct _GstTagLibMux { typedef struct _GstTagLibMux {
GstElement element; GstElement element;
GstPad *sinkpad, *srcpad; GstPad *srcpad;
GstTagList *tags; GstPad *sinkpad;
GstTagList *event_tags; /* tags received from upstream elements */
gsize tag_size; gsize tag_size;
gboolean render_tag; gboolean render_tag;
GstEvent *newsegment_ev; /* cached newsegment event from upstream */
} GstTagLibMux; } GstTagLibMux;
/* Standard definition defining a class for this element. */ /* Standard definition defining a class for this element. */