static GstTagList * gst_id3demux_merge_tags (GstTagDemux * tagdemux, const GstTagList * start_tags, const GstTagList * end_tags) { GstID3Demux *id3demux; GstTagList *merged; gboolean prefer_v1; id3demux = GST_ID3DEMUX (tagdemux); GST_OBJECT_LOCK (id3demux); prefer_v1 = id3demux->prefer_v1; GST_OBJECT_UNLOCK (id3demux); /* we merge in REPLACE mode, so put the less important tags first */ if (prefer_v1) merged = gst_tag_list_merge (start_tags, end_tags, GST_TAG_MERGE_REPLACE); else merged = gst_tag_list_merge (end_tags, start_tags, GST_TAG_MERGE_REPLACE); GST_LOG_OBJECT (id3demux, "start tags: %" GST_PTR_FORMAT, start_tags); GST_LOG_OBJECT (id3demux, "end tags: %" GST_PTR_FORMAT, end_tags); GST_LOG_OBJECT (id3demux, "merged tags: %" GST_PTR_FORMAT, merged); return merged; }
static void gst_shout2send_set_metadata (GstShout2send * shout2send) { const GstTagList *user_tags; GstTagList *copy; char *tempmetadata; shout_metadata_t *pmetadata; g_return_if_fail (shout2send != NULL); user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (shout2send)); if ((shout2send->tags == NULL) && (user_tags == NULL)) { return; } copy = gst_tag_list_merge (user_tags, shout2send->tags, gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (shout2send))); /* lets get the artist and song tags */ tempmetadata = NULL; gst_tag_list_foreach ((GstTagList *) copy, set_shout_metadata, (gpointer) & tempmetadata); if (tempmetadata) { pmetadata = shout_metadata_new (); shout_metadata_add (pmetadata, "song", tempmetadata); shout_set_metadata (shout2send->conn, pmetadata); shout_metadata_free (pmetadata); } gst_tag_list_unref (copy); }
static GstBuffer * gst_speex_enc_create_metadata_buffer (GstSpeexEnc * enc) { const GstTagList *user_tags; GstTagList *merged_tags; GstBuffer *comments = NULL; user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc)); GST_DEBUG_OBJECT (enc, "upstream tags = %" GST_PTR_FORMAT, enc->tags); GST_DEBUG_OBJECT (enc, "user-set tags = %" GST_PTR_FORMAT, user_tags); /* gst_tag_list_merge() will handle NULL for either or both lists fine */ merged_tags = gst_tag_list_merge (user_tags, enc->tags, gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (enc))); if (merged_tags == NULL) merged_tags = gst_tag_list_new (); GST_DEBUG_OBJECT (enc, "merged tags = %" GST_PTR_FORMAT, merged_tags); comments = gst_tag_list_to_vorbiscomment_buffer (merged_tags, NULL, 0, "Encoded with GStreamer Speexenc"); gst_tag_list_free (merged_tags); GST_BUFFER_OFFSET (comments) = enc->bytes_out; GST_BUFFER_OFFSET_END (comments) = 0; return comments; }
static gboolean _event_probe (GstPad * pad, GstEvent * event, PrivateStream * ps) { if (GST_EVENT_TYPE (event) == GST_EVENT_TAG) { GstTagList *tl = NULL, *tmp; gst_event_parse_tag (event, &tl); GST_DEBUG_OBJECT (pad, "tags %" GST_PTR_FORMAT, tl); DISCO_LOCK (ps->dc); /* If preroll is complete, drop these tags - the collected information is * possibly already being processed and adding more tags would be racy */ if (G_LIKELY (ps->dc->priv->processing)) { GST_DEBUG_OBJECT (pad, "private stream %p old tags %" GST_PTR_FORMAT, ps, ps->tags); tmp = gst_tag_list_merge (ps->tags, tl, GST_TAG_MERGE_APPEND); if (ps->tags) gst_tag_list_free (ps->tags); ps->tags = tmp; GST_DEBUG_OBJECT (pad, "private stream %p new tags %" GST_PTR_FORMAT, ps, tmp); } else GST_DEBUG_OBJECT (pad, "Dropping tags since preroll is done"); DISCO_UNLOCK (ps->dc); } return TRUE; }
/** * gst_toc_merge_tags: * @toc: A #GstToc instance * @tags: (allow-none): A #GstTagList or %NULL * @mode: A #GstTagMergeMode * * Merge @tags into the existing tags of @toc using @mode. */ void gst_toc_merge_tags (GstToc * toc, GstTagList * tags, GstTagMergeMode mode) { g_return_if_fail (toc != NULL); g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (toc))); if (!toc->tags) { toc->tags = gst_tag_list_ref (tags); } else { GstTagList *tmp = gst_tag_list_merge (toc->tags, tags, mode); gst_tag_list_unref (toc->tags); toc->tags = tmp; } }
GST_END_TEST GST_START_TEST (test_merge_modes) { GstTagMergeMode mode; for (mode = GST_TAG_MERGE_REPLACE_ALL; mode < GST_TAG_MERGE_COUNT; mode++) { gint i; for (i = 0; i < 4; i++) { GstElement *enc; GstTagSetter *setter; GstTagList *list1, *list2, *merged; enc = g_object_new (GST_TYPE_DUMMY_ENC, NULL); fail_unless (enc != NULL); setter = GST_TAG_SETTER (enc); list1 = gst_tag_list_new_empty (); list2 = gst_tag_list_new_empty (); /* i = 0: - - * i = 1: list1 - * i = 2: - list2 * i = 3: list1 list2 */ if (i % 2 == 1) { gst_tag_list_add (list1, GST_TAG_MERGE_APPEND, GST_TAG_ARTIST, "artist1", NULL); } if (i > 1) { gst_tag_list_add (list2, GST_TAG_MERGE_APPEND, GST_TAG_ARTIST, "artist2", NULL); } gst_tag_setter_merge_tags (setter, list1, GST_TAG_MERGE_APPEND); gst_tag_setter_merge_tags (setter, list2, mode); merged = gst_tag_list_merge (list1, list2, mode); fail_unless_equals_int (tag_list_length (gst_tag_setter_get_tag_list (setter)), tag_list_length (merged)); gst_tag_list_unref (list1); gst_tag_list_unref (list2); gst_tag_list_unref (merged); gst_object_unref (enc); } } }
/** * gst_toc_entry_merge_tags: * @entry: A #GstTocEntry instance * @tags: (allow-none): A #GstTagList or %NULL * @mode: A #GstTagMergeMode * * Merge @tags into the existing tags of @entry using @mode. */ void gst_toc_entry_merge_tags (GstTocEntry * entry, GstTagList * tags, GstTagMergeMode mode) { g_return_if_fail (entry != NULL); g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (entry))); if (!entry->tags) { entry->tags = gst_tag_list_ref (tags); } else { GstTagList *tmp = gst_tag_list_merge (entry->tags, tags, mode); gst_tag_list_unref (entry->tags); entry->tags = tmp; } }
static gboolean bus_handler (GstBus * bus, GstMessage * message, gpointer data) { GMainLoop *loop = (GMainLoop *) data; switch (message->type) { case GST_MESSAGE_EOS: g_main_loop_quit (loop); break; case GST_MESSAGE_WARNING: case GST_MESSAGE_ERROR:{ GError *gerror; gchar *debug; gst_message_parse_error (message, &gerror, &debug); gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug); g_error_free (gerror); g_free (debug); g_main_loop_quit (loop); break; } case GST_MESSAGE_TAG:{ if (received_tags == NULL) { gst_message_parse_tag (message, &received_tags); } else { GstTagList *tl = NULL, *ntl = NULL; gst_message_parse_tag (message, &tl); if (tl) { ntl = gst_tag_list_merge (received_tags, tl, GST_TAG_MERGE_PREPEND); if (ntl) { GST_LOG ("taglists merged: %" GST_PTR_FORMAT, ntl); gst_tag_list_free (received_tags); received_tags = ntl; } gst_tag_list_free (tl); } } break; } default: break; } return TRUE; }
void gst_kate_util_decoder_base_add_tags (GstKateDecoderBase * decoder, GstTagList * tags, gboolean take_ownership_of_tags) { if (!decoder->tags) { if (!take_ownership_of_tags) tags = gst_tag_list_ref (tags); decoder->tags = tags; } else { GstTagList *old = decoder->tags; decoder->tags = gst_tag_list_merge (old, tags, GST_TAG_MERGE_REPLACE); gst_tag_list_unref (old); if (take_ownership_of_tags) gst_tag_list_unref (tags); } decoder->tags_changed = TRUE; }
static GstFlowReturn gst_vorbis_tag_parse_packet (GstVorbisParse * parse, GstBuffer * buffer) { GstTagList *old_tags, *new_tags; const GstTagList *user_tags; GstVorbisTag *tagger; gchar *encoder = NULL; GstBuffer *new_buf; GstMapInfo map; gboolean do_parse = FALSE; gst_buffer_map (buffer, &map, GST_MAP_READ); /* just pass everything except the comments packet */ if (map.size >= 1 && map.data[0] != 0x03) do_parse = TRUE; gst_buffer_unmap (buffer, &map); if (do_parse) { return GST_VORBIS_PARSE_CLASS (parent_class)->parse_packet (parse, buffer); } tagger = GST_VORBIS_TAG (parse); old_tags = gst_tag_list_from_vorbiscomment_buffer (buffer, (guint8 *) "\003vorbis", 7, &encoder); user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (tagger)); /* build new tag list */ new_tags = gst_tag_list_merge (user_tags, old_tags, gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (tagger))); gst_tag_list_unref (old_tags); new_buf = gst_tag_list_to_vorbiscomment_buffer (new_tags, (guint8 *) "\003vorbis", 7, encoder); gst_buffer_copy_into (new_buf, buffer, GST_BUFFER_COPY_TIMESTAMPS, 0, -1); gst_tag_list_unref (new_tags); g_free (encoder); gst_buffer_unref (buffer); return GST_VORBIS_PARSE_CLASS (parent_class)->parse_packet (parse, new_buf); }
static GstFlowReturn gst_wavenc_write_tags (GstWavEnc * wavenc) { const GstTagList *user_tags; GstTagList *tags; guint size; GstBuffer *buf; GstByteWriter bw; g_return_val_if_fail (wavenc != NULL, GST_FLOW_OK); 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))); GST_DEBUG_OBJECT (wavenc, "writing tags"); gst_byte_writer_init_with_size (&bw, 1024, FALSE); /* 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); /* add tags */ gst_tag_list_foreach (tags, gst_wavparse_tags_foreach, &bw); /* 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); gst_tag_list_unref (tags); buf = gst_byte_writer_reset_and_get_buffer (&bw); wavenc->meta_length += gst_buffer_get_size (buf); return gst_pad_push (wavenc->srcpad, buf); }
/** * gst_aggregator_merge_tags: * @self: a #GstAggregator * @tags: a #GstTagList to merge * @mode: the #GstTagMergeMode to use * * Adds tags to so-called pending tags, which will be processed * before pushing out data downstream. * * Note that this is provided for convenience, and the subclass is * not required to use this and can still do tag handling on its own. * * MT safe. */ void gst_aggregator_merge_tags (GstAggregator * self, const GstTagList * tags, GstTagMergeMode mode) { GstTagList *otags; g_return_if_fail (GST_IS_AGGREGATOR (self)); g_return_if_fail (tags == NULL || GST_IS_TAG_LIST (tags)); /* FIXME Check if we can use OBJECT lock here! */ GST_OBJECT_LOCK (self); if (tags) GST_DEBUG_OBJECT (self, "merging tags %" GST_PTR_FORMAT, tags); otags = self->priv->tags; self->priv->tags = gst_tag_list_merge (self->priv->tags, tags, mode); if (otags) gst_tag_list_unref (otags); self->priv->tags_changed = TRUE; GST_OBJECT_UNLOCK (self); }
static void gst_kate_enc_set_metadata (GstKateEnc * ke) { GstTagList *merged_tags; const GstTagList *user_tags; user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (ke)); GST_DEBUG_OBJECT (ke, "upstream tags = %" GST_PTR_FORMAT, ke->tags); GST_DEBUG_OBJECT (ke, "user-set tags = %" GST_PTR_FORMAT, user_tags); /* gst_tag_list_merge() will handle NULL for either or both lists fine */ merged_tags = gst_tag_list_merge (user_tags, ke->tags, gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (ke))); if (merged_tags) { GST_DEBUG_OBJECT (ke, "merged tags = %" GST_PTR_FORMAT, merged_tags); gst_tag_list_foreach (merged_tags, gst_kate_enc_metadata_set1, ke); gst_tag_list_unref (merged_tags); } }
static void gst_vorbis_enc_set_metadata (GstVorbisEnc * enc) { GstTagList *merged_tags; const GstTagList *user_tags; vorbis_comment_init (&enc->vc); user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc)); GST_DEBUG_OBJECT (enc, "upstream tags = %" GST_PTR_FORMAT, enc->tags); GST_DEBUG_OBJECT (enc, "user-set tags = %" GST_PTR_FORMAT, user_tags); /* gst_tag_list_merge() will handle NULL for either or both lists fine */ merged_tags = gst_tag_list_merge (user_tags, enc->tags, gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (enc))); if (merged_tags) { GST_DEBUG_OBJECT (enc, "merged tags = %" GST_PTR_FORMAT, merged_tags); gst_tag_list_foreach (merged_tags, gst_vorbis_enc_metadata_set1, enc); gst_tag_list_unref (merged_tags); } }
static GstTagList * gst_tag_mux_get_tags (GstTagMux * mux) { GstTagSetter *tagsetter = GST_TAG_SETTER (mux); const GstTagList *tagsetter_tags; GstTagMergeMode merge_mode; if (mux->priv->final_tags) return mux->priv->final_tags; tagsetter_tags = gst_tag_setter_get_tag_list (tagsetter); merge_mode = gst_tag_setter_get_tag_merge_mode (tagsetter); GST_LOG_OBJECT (mux, "merging tags, merge mode = %d", merge_mode); GST_LOG_OBJECT (mux, "event tags: %" GST_PTR_FORMAT, mux->priv->event_tags); GST_LOG_OBJECT (mux, "set tags: %" GST_PTR_FORMAT, tagsetter_tags); mux->priv->final_tags = gst_tag_list_merge (tagsetter_tags, mux->priv->event_tags, merge_mode); GST_LOG_OBJECT (mux, "final tags: %" GST_PTR_FORMAT, mux->priv->final_tags); return mux->priv->final_tags; }
GstFlowReturn gst_kate_util_decoder_base_chain_kate_packet (GstKateDecoderBase * decoder, GstElement * element, GstPad * pad, GstBuffer * buf, GstPad * srcpad, GstPad * tagpad, GstCaps ** src_caps, const kate_event ** ev) { kate_packet kp; int ret; GstFlowReturn rflow = GST_FLOW_OK; gboolean is_header; guint8 *data; gsize size; guint8 header[1]; size = gst_buffer_extract (buf, 0, header, 1); GST_DEBUG_OBJECT (element, "got kate packet, %u bytes, type %02x", gst_buffer_get_size (buf), size == 0 ? -1 : header[0]); is_header = size > 0 && (header[0] & 0x80); if (!is_header && decoder->tags) { /* after we've processed headers, send any tags before processing the data packet */ GST_DEBUG_OBJECT (element, "Not a header, sending tags for pad %s:%s", GST_DEBUG_PAD_NAME (tagpad)); gst_element_found_tags_for_pad (element, tagpad, decoder->tags); decoder->tags = NULL; } data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ); kate_packet_wrap (&kp, size, data); ret = kate_high_decode_packetin (&decoder->k, &kp, ev); gst_buffer_unmap (buf, data, size); if (G_UNLIKELY (ret < 0)) { GST_ELEMENT_ERROR (element, STREAM, DECODE, (NULL), ("Failed to decode Kate packet: %s", gst_kate_util_get_error_message (ret))); return GST_FLOW_ERROR; } if (G_UNLIKELY (ret > 0)) { GST_DEBUG_OBJECT (element, "kate_high_decode_packetin has received EOS packet"); } /* headers may be interesting to retrieve information from */ if (G_UNLIKELY (is_header)) { switch (header[0]) { case 0x80: /* ID header */ GST_INFO_OBJECT (element, "Parsed ID header: language %s, category %s", decoder->k.ki->language, decoder->k.ki->category); if (src_caps) { if (*src_caps) { gst_caps_unref (*src_caps); *src_caps = NULL; } if (strcmp (decoder->k.ki->category, "K-SPU") == 0 || strcmp (decoder->k.ki->category, "spu-subtitles") == 0) { *src_caps = gst_caps_new_empty_simple ("video/x-dvd-subpicture"); } else if (decoder->k.ki->text_markup_type == kate_markup_none) { *src_caps = gst_caps_new_empty_simple ("text/plain"); } else { *src_caps = gst_caps_new_empty_simple ("text/x-pango-markup"); } GST_INFO_OBJECT (srcpad, "Setting caps: %" GST_PTR_FORMAT, *src_caps); if (!gst_pad_set_caps (srcpad, *src_caps)) { GST_ERROR_OBJECT (srcpad, "Failed to set caps %" GST_PTR_FORMAT, *src_caps); } } if (decoder->k.ki->language && *decoder->k.ki->language) { GstTagList *old = decoder->tags, *tags = gst_tag_list_new_empty (); if (tags) { gchar *lang_code; /* en_GB -> en */ lang_code = g_ascii_strdown (decoder->k.ki->language, -1); g_strdelimit (lang_code, NULL, '\0'); gst_tag_list_add (tags, GST_TAG_MERGE_APPEND, GST_TAG_LANGUAGE_CODE, lang_code, NULL); g_free (lang_code); /* TODO: category - where should it go ? */ decoder->tags = gst_tag_list_merge (decoder->tags, tags, GST_TAG_MERGE_REPLACE); gst_tag_list_free (tags); if (old) gst_tag_list_free (old); } } /* update properties */ if (decoder->language) g_free (decoder->language); decoder->language = g_strdup (decoder->k.ki->language); if (decoder->category) g_free (decoder->category); decoder->category = g_strdup (decoder->k.ki->category); decoder->original_canvas_width = decoder->k.ki->original_canvas_width; decoder->original_canvas_height = decoder->k.ki->original_canvas_height; /* we can now send away any event we've delayed, as the src pad now has caps */ gst_kate_util_decoder_base_drain_event_queue (decoder); break; case 0x81: /* Vorbis comments header */ GST_INFO_OBJECT (element, "Parsed comments header"); { gchar *encoder = NULL; GstTagList *old = decoder->tags, *list = gst_tag_list_from_vorbiscomment_buffer (buf, (const guint8 *) "\201kate\0\0\0\0", 9, &encoder); if (list) { decoder->tags = gst_tag_list_merge (decoder->tags, list, GST_TAG_MERGE_REPLACE); gst_tag_list_free (list); } if (!decoder->tags) { GST_ERROR_OBJECT (element, "failed to decode comment header"); decoder->tags = gst_tag_list_new_empty (); } if (encoder) { gst_tag_list_add (decoder->tags, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, encoder, NULL); g_free (encoder); } gst_tag_list_add (decoder->tags, GST_TAG_MERGE_REPLACE, GST_TAG_SUBTITLE_CODEC, "Kate", NULL); gst_tag_list_add (decoder->tags, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER_VERSION, decoder->k.ki->bitstream_version_major, NULL); if (old) gst_tag_list_free (old); if (decoder->initialized) { gst_element_found_tags_for_pad (element, tagpad, decoder->tags); decoder->tags = NULL; } else { /* Only push them as messages for the time being. * * They will be pushed on the pad once the decoder is initialized */ gst_element_post_message (element, gst_message_new_tag (GST_OBJECT (element), gst_tag_list_copy (decoder->tags))); } } break; default: break; } } #if ((KATE_VERSION_MAJOR<<16)|(KATE_VERSION_MINOR<<8)|KATE_VERSION_PATCH) >= 0x000400 else if (*ev && (*ev)->meta) { int count = kate_meta_query_count ((*ev)->meta); if (count > 0) { GstTagList *evtags = gst_tag_list_new_empty (); int idx; GST_DEBUG_OBJECT (decoder, "Kate event has %d attached metadata", count); for (idx = 0; idx < count; ++idx) { const char *tag, *value; size_t len; if (kate_meta_query ((*ev)->meta, idx, &tag, &value, &len) < 0) { GST_WARNING_OBJECT (decoder, "Failed to retrieve metadata %d", idx); } else { if (gst_kate_util_is_utf8_string (value, len)) { gchar *compound = g_strdup_printf ("%s=%s", tag, value); GST_DEBUG_OBJECT (decoder, "Metadata %d: %s=%s (%zu bytes)", idx, tag, value, len); gst_tag_list_add (evtags, GST_TAG_MERGE_APPEND, GST_TAG_EXTENDED_COMMENT, compound, NULL); g_free (compound); } else { GST_INFO_OBJECT (decoder, "Metadata %d, (%s, %zu bytes) is binary, ignored", idx, tag, len); } } } if (gst_tag_list_is_empty (evtags)) gst_tag_list_free (evtags); else gst_element_found_tags_for_pad (element, tagpad, evtags); } } #endif return rflow; }
static gboolean message_loop_to_state_change (MetadataExtractor *extractor, GstState state) { GstBus *bus; GstMessageType events; g_return_val_if_fail (extractor, FALSE); g_return_val_if_fail (extractor->playbin, FALSE); bus = gst_element_get_bus (extractor->playbin); events = (GST_MESSAGE_TAG | GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS); for (;;) { GstMessage *message; message = gst_bus_timed_pop_filtered (bus, GST_SECOND * 5, events); if (message == NULL) goto timed_out; switch (GST_MESSAGE_TYPE (message)) { case GST_MESSAGE_STATE_CHANGED: { GstState old_state; GstState new_state; old_state = new_state = GST_STATE_NULL; gst_message_parse_state_changed (message, &old_state, &new_state, NULL); if (old_state == new_state) break; /* we only care about playbin (pipeline) state changes */ if (GST_MESSAGE_SRC (message) != GST_OBJECT (extractor->playbin)) break; if ((old_state == GST_STATE_READY) && (new_state == GST_STATE_PAUSED)) update_stream_info (extractor); else if ((old_state == GST_STATE_PAUSED) && (new_state == GST_STATE_READY)) reset_extractor_data (extractor); if (new_state == state) { gst_message_unref (message); goto success; } break; } case GST_MESSAGE_TAG: { GstTagList *tag_list; GstTagList *result; tag_list = NULL; gst_message_parse_tag (message, &tag_list); result = gst_tag_list_merge (extractor->tagcache, tag_list, GST_TAG_MERGE_KEEP); if (extractor->tagcache != NULL) gst_tag_list_free (extractor->tagcache); extractor->tagcache = result; gst_tag_list_free (tag_list); break; } case GST_MESSAGE_ERROR: { gchar *debug = NULL; GError *gsterror = NULL; gst_message_parse_error (message, &gsterror, &debug); /*g_warning ("Error: %s (%s)", gsterror->message, debug);*/ g_error_free (gsterror); gst_message_unref (message); g_free (debug); goto error; } break; case GST_MESSAGE_EOS: { g_warning ("Media file could not be played."); gst_message_unref (message); goto error; } break; default: g_assert_not_reached (); break; } gst_message_unref (message); } g_assert_not_reached (); success: /* state change succeeded */ GST_DEBUG ("state change to %s succeeded", gst_element_state_get_name (state)); return TRUE; timed_out: /* it's taking a long time to open */ GST_DEBUG ("state change to %s timed out, returning success", gst_element_state_get_name (state)); return TRUE; error: GST_DEBUG ("error while waiting for state change to %s", gst_element_state_get_name (state)); /* already set *error */ return FALSE; }
void eServiceMP3::gstBusCall(GstMessage *msg) { if (!msg) return; gchar *sourceName; GstObject *source; source = GST_MESSAGE_SRC(msg); if (!GST_IS_OBJECT(source)) return; sourceName = gst_object_get_name(source); #if 0 gchar *string; if (gst_message_get_structure(msg)) string = gst_structure_to_string(gst_message_get_structure(msg)); else string = g_strdup(GST_MESSAGE_TYPE_NAME(msg)); eDebug("eTsRemoteSource::gst_message from %s: %s", sourceName, string); g_free(string); #endif switch (GST_MESSAGE_TYPE (msg)) { case GST_MESSAGE_EOS: m_event((iPlayableService*)this, evEOF); break; case GST_MESSAGE_STATE_CHANGED: { if(GST_MESSAGE_SRC(msg) != GST_OBJECT(m_gst_playbin)) break; GstState old_state, new_state; gst_message_parse_state_changed(msg, &old_state, &new_state, NULL); if(old_state == new_state) break; eDebug("eServiceMP3::state transition %s -> %s", gst_element_state_get_name(old_state), gst_element_state_get_name(new_state)); GstStateChange transition = (GstStateChange)GST_STATE_TRANSITION(old_state, new_state); switch(transition) { case GST_STATE_CHANGE_NULL_TO_READY: { } break; case GST_STATE_CHANGE_READY_TO_PAUSED: { GstElement *subsink = gst_bin_get_by_name(GST_BIN(m_gst_playbin), "subtitle_sink"); if (subsink) { #ifdef GSTREAMER_SUBTITLE_SYNC_MODE_BUG /* * HACK: disable sync mode for now, gstreamer suffers from a bug causing sparse streams to loose sync, after pause/resume / skip * see: https://bugzilla.gnome.org/show_bug.cgi?id=619434 * Sideeffect of using sync=false is that we receive subtitle buffers (far) ahead of their * display time. * Not too far ahead for subtitles contained in the media container. * But for external srt files, we could receive all subtitles at once. * And not just once, but after each pause/resume / skip. * So as soon as gstreamer has been fixed to keep sync in sparse streams, sync needs to be re-enabled. */ g_object_set (G_OBJECT (subsink), "sync", FALSE, NULL); #endif #if 0 /* we should not use ts-offset to sync with the decoder time, we have to do our own decoder timekeeping */ g_object_set (G_OBJECT (subsink), "ts-offset", -2L * GST_SECOND, NULL); /* late buffers probably will not occur very often */ g_object_set (G_OBJECT (subsink), "max-lateness", 0L, NULL); /* avoid prerolling (it might not be a good idea to preroll a sparse stream) */ g_object_set (G_OBJECT (subsink), "async", TRUE, NULL); #endif eDebug("eServiceMP3::subsink properties set!"); gst_object_unref(subsink); } setAC3Delay(ac3_delay); setPCMDelay(pcm_delay); } break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: { if ( m_sourceinfo.is_streaming && m_streamingsrc_timeout ) m_streamingsrc_timeout->stop(); } break; case GST_STATE_CHANGE_PLAYING_TO_PAUSED: { } break; case GST_STATE_CHANGE_PAUSED_TO_READY: { } break; case GST_STATE_CHANGE_READY_TO_NULL: { } break; } break; } case GST_MESSAGE_ERROR: { gchar *debug; GError *err; gst_message_parse_error (msg, &err, &debug); g_free (debug); eWarning("Gstreamer error: %s (%i) from %s", err->message, err->code, sourceName ); if ( err->domain == GST_STREAM_ERROR ) { if ( err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND ) { if ( g_strrstr(sourceName, "videosink") ) m_event((iPlayableService*)this, evUser+11); else if ( g_strrstr(sourceName, "audiosink") ) m_event((iPlayableService*)this, evUser+10); } } g_error_free(err); break; } case GST_MESSAGE_INFO: { gchar *debug; GError *inf; gst_message_parse_info (msg, &inf, &debug); g_free (debug); if ( inf->domain == GST_STREAM_ERROR && inf->code == GST_STREAM_ERROR_DECODE ) { if ( g_strrstr(sourceName, "videosink") ) m_event((iPlayableService*)this, evUser+14); } g_error_free(inf); break; } case GST_MESSAGE_TAG: { GstTagList *tags, *result; gst_message_parse_tag(msg, &tags); result = gst_tag_list_merge(m_stream_tags, tags, GST_TAG_MERGE_REPLACE); if (result) { if (m_stream_tags) gst_tag_list_free(m_stream_tags); m_stream_tags = result; } const GValue *gv_image = gst_tag_list_get_value_index(tags, GST_TAG_IMAGE, 0); if ( gv_image ) { GstBuffer *buf_image; buf_image = gst_value_get_buffer (gv_image); int fd = open("/tmp/.id3coverart", O_CREAT|O_WRONLY|O_TRUNC, 0644); int ret = write(fd, GST_BUFFER_DATA(buf_image), GST_BUFFER_SIZE(buf_image)); close(fd); eDebug("eServiceMP3::/tmp/.id3coverart %d bytes written ", ret); m_event((iPlayableService*)this, evUser+13); } gst_tag_list_free(tags); m_event((iPlayableService*)this, evUpdatedInfo); break; } case GST_MESSAGE_ASYNC_DONE: { if(GST_MESSAGE_SRC(msg) != GST_OBJECT(m_gst_playbin)) break; GstTagList *tags; gint i, active_idx, n_video = 0, n_audio = 0, n_text = 0; g_object_get (m_gst_playbin, "n-video", &n_video, NULL); g_object_get (m_gst_playbin, "n-audio", &n_audio, NULL); g_object_get (m_gst_playbin, "n-text", &n_text, NULL); eDebug("eServiceMP3::async-done - %d video, %d audio, %d subtitle", n_video, n_audio, n_text); if ( n_video + n_audio <= 0 ) stop(); active_idx = 0; m_audioStreams.clear(); m_subtitleStreams.clear(); for (i = 0; i < n_audio; i++) { audioStream audio; gchar *g_codec, *g_lang; GstPad* pad = 0; g_signal_emit_by_name (m_gst_playbin, "get-audio-pad", i, &pad); GstCaps* caps = gst_pad_get_negotiated_caps(pad); if (!caps) continue; GstStructure* str = gst_caps_get_structure(caps, 0); const gchar *g_type = gst_structure_get_name(str); eDebug("AUDIO STRUCT=%s", g_type); audio.type = gstCheckAudioPad(str); g_codec = g_strdup(g_type); g_lang = g_strdup_printf ("und"); g_signal_emit_by_name (m_gst_playbin, "get-audio-tags", i, &tags); if ( tags && gst_is_tag_list(tags) ) { gst_tag_list_get_string(tags, GST_TAG_AUDIO_CODEC, &g_codec); gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &g_lang); gst_tag_list_free(tags); } audio.language_code = std::string(g_lang); audio.codec = std::string(g_codec); eDebug("eServiceMP3::audio stream=%i codec=%s language=%s", i, g_codec, g_lang); m_audioStreams.push_back(audio); g_free (g_lang); g_free (g_codec); gst_caps_unref(caps); } for (i = 0; i < n_text; i++) { gchar *g_codec = NULL, *g_lang = NULL; g_signal_emit_by_name (m_gst_playbin, "get-text-tags", i, &tags); subtitleStream subs; g_lang = g_strdup_printf ("und"); if ( tags && gst_is_tag_list(tags) ) { gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &g_lang); gst_tag_list_get_string(tags, GST_TAG_SUBTITLE_CODEC, &g_codec); gst_tag_list_free(tags); } subs.language_code = std::string(g_lang); eDebug("eServiceMP3::subtitle stream=%i language=%s codec=%s", i, g_lang, g_codec); GstPad* pad = 0; g_signal_emit_by_name (m_gst_playbin, "get-text-pad", i, &pad); if ( pad ) g_signal_connect (G_OBJECT (pad), "notify::caps", G_CALLBACK (gstTextpadHasCAPS), this); subs.type = getSubtitleType(pad, g_codec); m_subtitleStreams.push_back(subs); g_free (g_lang); } m_event((iPlayableService*)this, evUpdatedInfo); if ( m_errorInfo.missing_codec != "" ) { if ( m_errorInfo.missing_codec.find("video/") == 0 || ( m_errorInfo.missing_codec.find("audio/") == 0 && getNumberOfTracks() == 0 ) ) m_event((iPlayableService*)this, evUser+12); } break; } case GST_MESSAGE_ELEMENT: { if (const GstStructure *msgstruct = gst_message_get_structure(msg)) { if ( gst_is_missing_plugin_message(msg) ) { GstCaps *caps; gst_structure_get (msgstruct, "detail", GST_TYPE_CAPS, &caps, NULL); std::string codec = (const char*) gst_caps_to_string(caps); gchar *description = gst_missing_plugin_message_get_description(msg); if ( description ) { eDebug("eServiceMP3::m_errorInfo.missing_codec = %s", codec.c_str()); m_errorInfo.error_message = "GStreamer plugin " + (std::string)description + " not available!\n"; m_errorInfo.missing_codec = codec.substr(0,(codec.find_first_of(','))); g_free(description); } gst_caps_unref(caps); } else { const gchar *eventname = gst_structure_get_name(msgstruct); if ( eventname ) { if (!strcmp(eventname, "eventSizeChanged") || !strcmp(eventname, "eventSizeAvail")) { gst_structure_get_int (msgstruct, "aspect_ratio", &m_aspect); gst_structure_get_int (msgstruct, "width", &m_width); gst_structure_get_int (msgstruct, "height", &m_height); if (strstr(eventname, "Changed")) m_event((iPlayableService*)this, evVideoSizeChanged); } else if (!strcmp(eventname, "eventFrameRateChanged") || !strcmp(eventname, "eventFrameRateAvail")) { gst_structure_get_int (msgstruct, "frame_rate", &m_framerate); if (strstr(eventname, "Changed")) m_event((iPlayableService*)this, evVideoFramerateChanged); } else if (!strcmp(eventname, "eventProgressiveChanged") || !strcmp(eventname, "eventProgressiveAvail")) { gst_structure_get_int (msgstruct, "progressive", &m_progressive); if (strstr(eventname, "Changed")) m_event((iPlayableService*)this, evVideoProgressiveChanged); } } } } break; } case GST_MESSAGE_BUFFERING: { GstBufferingMode mode; gst_message_parse_buffering(msg, &(m_bufferInfo.bufferPercent)); gst_message_parse_buffering_stats(msg, &mode, &(m_bufferInfo.avgInRate), &(m_bufferInfo.avgOutRate), &(m_bufferInfo.bufferingLeft)); m_event((iPlayableService*)this, evBuffering); break; } case GST_MESSAGE_STREAM_STATUS: { GstStreamStatusType type; GstElement *owner; gst_message_parse_stream_status (msg, &type, &owner); if ( type == GST_STREAM_STATUS_TYPE_CREATE && m_sourceinfo.is_streaming ) { if ( GST_IS_PAD(source) ) owner = gst_pad_get_parent_element(GST_PAD(source)); else if ( GST_IS_ELEMENT(source) ) owner = GST_ELEMENT(source); else owner = 0; if ( owner ) { GstElementFactory *factory = gst_element_get_factory(GST_ELEMENT(owner)); const gchar *name = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory)); if (!strcmp(name, "souphttpsrc")) { m_streamingsrc_timeout->start(HTTP_TIMEOUT*1000, true); g_object_set (G_OBJECT (owner), "timeout", HTTP_TIMEOUT, NULL); eDebug("eServiceMP3::GST_STREAM_STATUS_TYPE_CREATE -> setting timeout on %s to %is", name, HTTP_TIMEOUT); } } if ( GST_IS_PAD(source) ) gst_object_unref(owner); } break; } default: break; } g_free (sourceName); }
static GstFlowReturn gst_flac_tag_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) { GstFlacTag *tag; GstFlowReturn ret; GstMapInfo map; gsize size; ret = GST_FLOW_OK; tag = GST_FLAC_TAG (parent); gst_adapter_push (tag->adapter, buffer); /* Initial state, we don't even know if we are dealing with a flac file */ if (tag->state == GST_FLAC_TAG_STATE_INIT) { GstBuffer *id_buffer; if (gst_adapter_available (tag->adapter) < sizeof (FLAC_MAGIC)) goto cleanup; id_buffer = gst_adapter_take_buffer (tag->adapter, FLAC_MAGIC_SIZE); GST_DEBUG_OBJECT (tag, "looking for " FLAC_MAGIC " identifier"); if (gst_buffer_memcmp (id_buffer, 0, FLAC_MAGIC, FLAC_MAGIC_SIZE) == 0) { GST_DEBUG_OBJECT (tag, "pushing " FLAC_MAGIC " identifier buffer"); ret = gst_pad_push (tag->srcpad, id_buffer); if (ret != GST_FLOW_OK) goto cleanup; tag->state = GST_FLAC_TAG_STATE_METADATA_BLOCKS; } else { /* FIXME: does that work well with FLAC files containing ID3v2 tags ? */ gst_buffer_unref (id_buffer); GST_ELEMENT_ERROR (tag, STREAM, WRONG_TYPE, (NULL), (NULL)); ret = GST_FLOW_ERROR; } } /* The fLaC magic string has been skipped, try to detect the beginning * of a metadata block */ if (tag->state == GST_FLAC_TAG_STATE_METADATA_BLOCKS) { guint type; gboolean is_last; const guint8 *block_header; g_assert (tag->metadata_block_size == 0); g_assert (tag->metadata_last_block == FALSE); /* The header of a flac metadata block is 4 bytes long: * 1st bit: indicates whether this is the last metadata info block * 7 next bits: 4 if vorbis comment block * 24 next bits: size of the metadata to follow (big endian) */ if (gst_adapter_available (tag->adapter) < 4) goto cleanup; block_header = gst_adapter_map (tag->adapter, 4); is_last = ((block_header[0] & 0x80) == 0x80); type = block_header[0] & 0x7F; size = (block_header[1] << 16) | (block_header[2] << 8) | block_header[3]; gst_adapter_unmap (tag->adapter); /* The 4 bytes long header isn't included in the metadata size */ tag->metadata_block_size = size + 4; tag->metadata_last_block = is_last; GST_DEBUG_OBJECT (tag, "got metadata block: %" G_GSIZE_FORMAT " bytes, type %d, " "is vorbiscomment: %d, is last: %d", size, type, (type == 0x04), is_last); /* Metadata blocks of type 4 are vorbis comment blocks */ if (type == 0x04) { tag->state = GST_FLAC_TAG_STATE_VC_METADATA_BLOCK; } else { tag->state = GST_FLAC_TAG_STATE_WRITING_METADATA_BLOCK; } } /* Reads a metadata block */ if ((tag->state == GST_FLAC_TAG_STATE_WRITING_METADATA_BLOCK) || (tag->state == GST_FLAC_TAG_STATE_VC_METADATA_BLOCK)) { GstBuffer *metadata_buffer; if (gst_adapter_available (tag->adapter) < tag->metadata_block_size) goto cleanup; metadata_buffer = gst_adapter_take_buffer (tag->adapter, tag->metadata_block_size); /* clear the is-last flag, as the last metadata block will * be the vorbis comment block which we will build ourselves. */ gst_buffer_map (metadata_buffer, &map, GST_MAP_READWRITE); map.data[0] &= (~0x80); gst_buffer_unmap (metadata_buffer, &map); if (tag->state == GST_FLAC_TAG_STATE_WRITING_METADATA_BLOCK) { GST_DEBUG_OBJECT (tag, "pushing metadata block buffer"); ret = gst_pad_push (tag->srcpad, metadata_buffer); if (ret != GST_FLOW_OK) goto cleanup; } else { tag->vorbiscomment = metadata_buffer; } tag->metadata_block_size = 0; tag->state = GST_FLAC_TAG_STATE_METADATA_NEXT_BLOCK; } /* This state is mainly used to be able to stop as soon as we read * a vorbiscomment block from the flac file if we are in an only output * tags mode */ if (tag->state == GST_FLAC_TAG_STATE_METADATA_NEXT_BLOCK) { /* Check if in the previous iteration we read a vorbis comment metadata * block, and stop now if the user only wants to read tags */ if (tag->vorbiscomment != NULL) { guint8 id_data[4]; /* We found some tags, try to parse them and notify the other elements * that we encountered some tags */ GST_DEBUG_OBJECT (tag, "emitting vorbiscomment tags"); gst_buffer_extract (tag->vorbiscomment, 0, id_data, 4); tag->tags = gst_tag_list_from_vorbiscomment_buffer (tag->vorbiscomment, id_data, 4, NULL); if (tag->tags != NULL) { gst_pad_push_event (tag->srcpad, gst_event_new_tag (gst_tag_list_copy (tag->tags))); } gst_buffer_unref (tag->vorbiscomment); tag->vorbiscomment = NULL; } /* Skip to next state */ if (tag->metadata_last_block == FALSE) { tag->state = GST_FLAC_TAG_STATE_METADATA_BLOCKS; } else { tag->state = GST_FLAC_TAG_STATE_ADD_VORBIS_COMMENT; } } /* Creates a vorbis comment block from the metadata which was set * on the gstreamer element, and add it to the flac stream */ if (tag->state == GST_FLAC_TAG_STATE_ADD_VORBIS_COMMENT) { GstBuffer *buffer; const GstTagList *user_tags; GstTagList *merged_tags; /* merge the tag lists */ user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (tag)); if (user_tags != NULL) { merged_tags = gst_tag_list_merge (user_tags, tag->tags, gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (tag))); } else { merged_tags = gst_tag_list_copy (tag->tags); } if (merged_tags == NULL) { /* If we get a NULL list of tags, we must generate a padding block * which is marked as the last metadata block, otherwise we'll * end up with a corrupted flac file. */ GST_WARNING_OBJECT (tag, "No tags found"); buffer = gst_buffer_new_and_alloc (12); if (buffer == NULL) goto no_buffer; gst_buffer_map (buffer, &map, GST_MAP_WRITE); memset (map.data, 0, map.size); map.data[0] = 0x81; /* 0x80 = Last metadata block, * 0x01 = padding block */ gst_buffer_unmap (buffer, &map); } else { guchar header[4]; guint8 fbit[1]; memset (header, 0, sizeof (header)); header[0] = 0x84; /* 0x80 = Last metadata block, * 0x04 = vorbiscomment block */ buffer = gst_tag_list_to_vorbiscomment_buffer (merged_tags, header, sizeof (header), NULL); GST_DEBUG_OBJECT (tag, "Writing tags %" GST_PTR_FORMAT, merged_tags); gst_tag_list_free (merged_tags); if (buffer == NULL) goto no_comment; size = gst_buffer_get_size (buffer); if ((size < 4) || ((size - 4) > 0xFFFFFF)) goto comment_too_long; fbit[0] = 1; /* Get rid of the framing bit at the end of the vorbiscomment buffer * if it exists since libFLAC seems to lose sync because of this * bit in gstflacdec */ if (gst_buffer_memcmp (buffer, size - 1, fbit, 1) == 0) { buffer = gst_buffer_make_writable (buffer); gst_buffer_resize (buffer, 0, size - 1); } } /* The 4 byte metadata block header isn't accounted for in the total * size of the metadata block */ gst_buffer_map (buffer, &map, GST_MAP_WRITE); map.data[1] = (((map.size - 4) & 0xFF0000) >> 16); map.data[2] = (((map.size - 4) & 0x00FF00) >> 8); map.data[3] = ((map.size - 4) & 0x0000FF); gst_buffer_unmap (buffer, &map); GST_DEBUG_OBJECT (tag, "pushing %" G_GSIZE_FORMAT " byte vorbiscomment " "buffer", map.size); ret = gst_pad_push (tag->srcpad, buffer); if (ret != GST_FLOW_OK) { goto cleanup; } tag->state = GST_FLAC_TAG_STATE_AUDIO_DATA; }
static gboolean gst_selector_pad_event (GstPad * pad, GstEvent * event) { gboolean res = TRUE; gboolean forward = TRUE; GstInputSelector *sel; GstSelectorPad *selpad; GstPad *prev_active_sinkpad; GstPad *active_sinkpad; sel = GST_INPUT_SELECTOR (gst_pad_get_parent (pad)); selpad = GST_SELECTOR_PAD_CAST (pad); GST_INPUT_SELECTOR_LOCK (sel); prev_active_sinkpad = sel->active_sinkpad; active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad); /* only forward if we are dealing with the active sinkpad or if select_all * is enabled */ if (pad != active_sinkpad && !sel->select_all) forward = FALSE; GST_INPUT_SELECTOR_UNLOCK (sel); if (prev_active_sinkpad != active_sinkpad && pad == active_sinkpad) g_object_notify (G_OBJECT (sel), "active-pad"); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_START: /* FIXME, flush out the waiter */ break; case GST_EVENT_FLUSH_STOP: GST_INPUT_SELECTOR_LOCK (sel); gst_selector_pad_reset (selpad); sel->pending_close = FALSE; GST_INPUT_SELECTOR_UNLOCK (sel); break; case GST_EVENT_NEWSEGMENT: { gboolean update; GstFormat format; gdouble rate, arate; gint64 start, stop, time; gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, &start, &stop, &time); GST_DEBUG_OBJECT (pad, "configured NEWSEGMENT update %d, rate %lf, applied rate %lf, " "format %d, " "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %" G_GINT64_FORMAT, update, rate, arate, format, start, stop, time); GST_INPUT_SELECTOR_LOCK (sel); GST_OBJECT_LOCK (selpad); gst_segment_set_newsegment_full (&selpad->segment, update, rate, arate, format, start, stop, time); GST_OBJECT_UNLOCK (selpad); /* If we aren't forwarding the event (because the pad is not the * active_sinkpad, and select_all is not set, then set the flag on the * that says a segment needs sending if/when that pad is activated. * For all other cases, we send the event immediately, which makes * sparse streams and other segment updates work correctly downstream. */ if (!forward) selpad->segment_pending = TRUE; GST_INPUT_SELECTOR_UNLOCK (sel); break; } case GST_EVENT_TAG: { GstTagList *tags, *oldtags, *newtags; gst_event_parse_tag (event, &tags); GST_OBJECT_LOCK (selpad); oldtags = selpad->tags; newtags = gst_tag_list_merge (oldtags, tags, GST_TAG_MERGE_REPLACE); selpad->tags = newtags; if (oldtags) gst_tag_list_free (oldtags); GST_DEBUG_OBJECT (pad, "received tags %" GST_PTR_FORMAT, newtags); GST_OBJECT_UNLOCK (selpad); g_object_notify (G_OBJECT (selpad), "tags"); break; } case GST_EVENT_EOS: selpad->eos = TRUE; GST_DEBUG_OBJECT (pad, "received EOS"); /* don't forward eos in select_all mode until all sink pads have eos */ if (sel->select_all && !gst_input_selector_check_eos (GST_ELEMENT (sel))) { forward = FALSE; } break; default: break; } if (forward) { GST_DEBUG_OBJECT (pad, "forwarding event"); res = gst_pad_push_event (sel->srcpad, event); } else gst_event_unref (event); gst_object_unref (sel); return res; }
GstBusSyncReply Gst_bus_call(GstBus * bus, GstMessage *msg, gpointer user_data) { gchar * sourceName; // source GstObject * source; source = GST_MESSAGE_SRC(msg); if (!GST_IS_OBJECT(source)) return GST_BUS_DROP; sourceName = gst_object_get_name(source); switch (GST_MESSAGE_TYPE(msg)) { case GST_MESSAGE_EOS: { g_message("End-of-stream"); end_eof = 1; break; } case GST_MESSAGE_ERROR: { gchar * debug; GError *err; gst_message_parse_error(msg, &err, &debug); g_free (debug); lt_info_c( "%s:%s - GST_MESSAGE_ERROR: %s (%i) from %s\n", FILENAME, __FUNCTION__, err->message, err->code, sourceName ); if ( err->domain == GST_STREAM_ERROR ) { if ( err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND ) { if ( g_strrstr(sourceName, "videosink") ) lt_info_c( "%s:%s - GST_MESSAGE_ERROR: videosink\n", FILENAME, __FUNCTION__ ); //FIXME: how shall playback handle this event??? else if ( g_strrstr(sourceName, "audiosink") ) lt_info_c( "%s:%s - GST_MESSAGE_ERROR: audioSink\n", FILENAME, __FUNCTION__ ); //FIXME: how shall playback handle this event??? } } g_error_free(err); end_eof = 1; // NOTE: just to exit break; } case GST_MESSAGE_INFO: { gchar *debug; GError *inf; gst_message_parse_info (msg, &inf, &debug); g_free (debug); if ( inf->domain == GST_STREAM_ERROR && inf->code == GST_STREAM_ERROR_DECODE ) { if ( g_strrstr(sourceName, "videosink") ) lt_info_c( "%s:%s - GST_MESSAGE_INFO: videosink\n", FILENAME, __FUNCTION__ ); //FIXME: how shall playback handle this event??? } g_error_free(inf); break; } case GST_MESSAGE_TAG: { GstTagList *tags, *result; gst_message_parse_tag(msg, &tags); result = gst_tag_list_merge(m_stream_tags, tags, GST_TAG_MERGE_REPLACE); if (result) { if (m_stream_tags) gst_tag_list_free(m_stream_tags); m_stream_tags = result; } const GValue *gv_image = gst_tag_list_get_value_index(tags, GST_TAG_IMAGE, 0); if ( gv_image ) { GstBuffer *buf_image; buf_image = gst_value_get_buffer (gv_image); int fd = open("/tmp/.id3coverart", O_CREAT|O_WRONLY|O_TRUNC, 0644); if(fd >= 0) { int ret = write(fd, GST_BUFFER_DATA(buf_image), GST_BUFFER_SIZE(buf_image)); close(fd); lt_info_c( "%s:%s - GST_MESSAGE_INFO: cPlayback::state /tmp/.id3coverart %d bytes written\n", FILENAME, __FUNCTION__ , ret); } //FIXME: how shall playback handle this event??? } gst_tag_list_free(tags); lt_info_c( "%s:%s - GST_MESSAGE_INFO: update info tags\n", FILENAME, __FUNCTION__); //FIXME: how shall playback handle this event??? break; } case GST_MESSAGE_STATE_CHANGED: { if(GST_MESSAGE_SRC(msg) != GST_OBJECT(m_gst_playbin)) break; GstState old_state, new_state; gst_message_parse_state_changed(msg, &old_state, &new_state, NULL); if(old_state == new_state) break; lt_info_c( "%s:%s - GST_MESSAGE_STATE_CHANGED: state transition %s -> %s\n", FILENAME, __FUNCTION__, gst_element_state_get_name(old_state), gst_element_state_get_name(new_state)); GstStateChange transition = (GstStateChange)GST_STATE_TRANSITION(old_state, new_state); switch(transition) { case GST_STATE_CHANGE_NULL_TO_READY: { } break; case GST_STATE_CHANGE_READY_TO_PAUSED: { GstIterator *children; if (audioSink) { gst_object_unref(GST_OBJECT(audioSink)); audioSink = NULL; } if (videoSink) { gst_object_unref(GST_OBJECT(videoSink)); videoSink = NULL; } children = gst_bin_iterate_recurse(GST_BIN(m_gst_playbin)); audioSink = GST_ELEMENT_CAST(gst_iterator_find_custom(children, (GCompareFunc)match_sinktype, (gpointer)"GstDVBAudioSink")); videoSink = GST_ELEMENT_CAST(gst_iterator_find_custom(children, (GCompareFunc)match_sinktype, (gpointer)"GstDVBVideoSink")); gst_iterator_free(children); } break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: { } break; case GST_STATE_CHANGE_PLAYING_TO_PAUSED: { } break; case GST_STATE_CHANGE_PAUSED_TO_READY: { if (audioSink) { gst_object_unref(GST_OBJECT(audioSink)); audioSink = NULL; } if (videoSink) { gst_object_unref(GST_OBJECT(videoSink)); videoSink = NULL; } } break; case GST_STATE_CHANGE_READY_TO_NULL: { } break; } break; } #if 0 case GST_MESSAGE_ELEMENT: { if(gst_structure_has_name(gst_message_get_structure(msg), "prepare-xwindow-id")) { // set window id gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(GST_MESSAGE_SRC (msg)), glfb->getWindowID()); // reshape window gst_x_overlay_set_render_rectangle(GST_X_OVERLAY(GST_MESSAGE_SRC (msg)), 0, 0, glfb->getOSDWidth(), glfb->getOSDHeight()); // sync frames gst_x_overlay_expose(GST_X_OVERLAY(GST_MESSAGE_SRC (msg))); } } #endif break; default: break; } return GST_BUS_DROP; }
/* If a parent is non-NULL, collected stream information will be appended to it * (and where the information exists, it will be overriden) */ static GstDiscovererStreamInfo * parse_stream_topology (GstDiscoverer * dc, const GstStructure * topology, GstDiscovererStreamInfo * parent) { GstDiscovererStreamInfo *res = NULL; GstCaps *caps = NULL; const GValue *nval = NULL; GST_DEBUG ("parsing: %" GST_PTR_FORMAT, topology); nval = gst_structure_get_value (topology, "next"); if (nval == NULL || GST_VALUE_HOLDS_STRUCTURE (nval)) { GstStructure *st = find_stream_for_node (dc, topology); gboolean add_to_list = TRUE; if (st) { res = collect_information (dc, st, parent); gst_structure_free (st); } else { /* Didn't find a stream structure, so let's just use the caps we have */ res = collect_information (dc, topology, parent); } if (nval == NULL) { /* FIXME : aggregate with information from main streams */ GST_DEBUG ("Coudn't find 'next' ! might be the last entry"); } else { GstCaps *caps; const GstStructure *st; st = gst_value_get_structure (nval); GST_DEBUG ("next is a structure %" GST_PTR_FORMAT, st); if (!parent) parent = res; if (gst_structure_id_get (st, _CAPS_QUARK, GST_TYPE_CAPS, &caps, NULL)) { if (gst_caps_can_intersect (parent->caps, caps)) { /* We sometimes get an extra sub-stream from the parser. If this is * the case, we just replace the parent caps with this stream's caps * since they might contain more information */ gst_caps_unref (parent->caps); parent->caps = caps; parse_stream_topology (dc, st, parent); add_to_list = FALSE; } else if (child_is_raw_stream (parent->caps, caps)) { /* This is the "raw" stream corresponding to the parent. This * contains more information than the parent, tags etc. */ parse_stream_topology (dc, st, parent); add_to_list = FALSE; gst_caps_unref (caps); } else { GstDiscovererStreamInfo *next = parse_stream_topology (dc, st, NULL); res->next = next; next->previous = res; } } } if (add_to_list) { dc->priv->current_info->stream_list = g_list_append (dc->priv->current_info->stream_list, res); } } else if (GST_VALUE_HOLDS_LIST (nval)) { guint i, len; GstDiscovererContainerInfo *cont; GstTagList *tags; if (!gst_structure_id_get (topology, _CAPS_QUARK, GST_TYPE_CAPS, &caps, NULL)) GST_WARNING ("Couldn't find caps !"); len = gst_value_list_get_size (nval); GST_DEBUG ("next is a list of %d entries", len); cont = (GstDiscovererContainerInfo *) gst_mini_object_new (GST_TYPE_DISCOVERER_CONTAINER_INFO); cont->parent.caps = caps; res = (GstDiscovererStreamInfo *) cont; if (gst_structure_id_has_field (topology, _TAGS_QUARK)) { GstTagList *tmp; gst_structure_id_get (topology, _TAGS_QUARK, GST_TYPE_STRUCTURE, &tags, NULL); GST_DEBUG ("Merge tags %" GST_PTR_FORMAT, tags); tmp = gst_tag_list_merge (cont->parent.tags, (GstTagList *) tags, GST_TAG_MERGE_APPEND); gst_tag_list_free (tags); if (cont->parent.tags) gst_tag_list_free (cont->parent.tags); cont->parent.tags = tmp; GST_DEBUG ("Container info tags %" GST_PTR_FORMAT, tmp); } for (i = 0; i < len; i++) { const GValue *subv = gst_value_list_get_value (nval, i); const GstStructure *subst = gst_value_get_structure (subv); GstDiscovererStreamInfo *substream; GST_DEBUG ("%d %" GST_PTR_FORMAT, i, subst); substream = parse_stream_topology (dc, subst, NULL); substream->previous = res; cont->streams = g_list_append (cont->streams, gst_discoverer_stream_info_ref (substream)); } } return res; }
/* Parses a set of caps and tags in st and populates a GstDiscovererStreamInfo * structure (parent, if !NULL, otherwise it allocates one) */ static GstDiscovererStreamInfo * collect_information (GstDiscoverer * dc, const GstStructure * st, GstDiscovererStreamInfo * parent) { GstCaps *caps; GstStructure *caps_st, *tags_st; const gchar *name; int tmp, tmp2; guint utmp; gboolean btmp; if (!st || !gst_structure_id_has_field (st, _CAPS_QUARK)) { GST_WARNING ("Couldn't find caps !"); if (parent) return parent; else return (GstDiscovererStreamInfo *) gst_mini_object_new (GST_TYPE_DISCOVERER_STREAM_INFO); } gst_structure_id_get (st, _CAPS_QUARK, GST_TYPE_CAPS, &caps, NULL); caps_st = gst_caps_get_structure (caps, 0); name = gst_structure_get_name (caps_st); if (g_str_has_prefix (name, "audio/")) { GstDiscovererAudioInfo *info; if (parent) info = (GstDiscovererAudioInfo *) parent; else { info = (GstDiscovererAudioInfo *) gst_mini_object_new (GST_TYPE_DISCOVERER_AUDIO_INFO); info->parent.caps = caps; } if (gst_structure_get_int (caps_st, "rate", &tmp)) info->sample_rate = (guint) tmp; if (gst_structure_get_int (caps_st, "channels", &tmp)) info->channels = (guint) tmp; if (gst_structure_get_int (caps_st, "depth", &tmp)) info->depth = (guint) tmp; if (gst_structure_id_has_field (st, _TAGS_QUARK)) { gst_structure_id_get (st, _TAGS_QUARK, GST_TYPE_STRUCTURE, &tags_st, NULL); if (gst_structure_get_uint (tags_st, GST_TAG_BITRATE, &utmp) || gst_structure_get_uint (tags_st, GST_TAG_NOMINAL_BITRATE, &utmp)) info->bitrate = utmp; if (gst_structure_get_uint (tags_st, GST_TAG_MAXIMUM_BITRATE, &utmp)) info->max_bitrate = utmp; /* FIXME: Is it worth it to remove the tags we've parsed? */ info->parent.tags = gst_tag_list_merge (info->parent.tags, (GstTagList *) tags_st, GST_TAG_MERGE_REPLACE); gst_structure_free (tags_st); } return (GstDiscovererStreamInfo *) info; } else if (g_str_has_prefix (name, "video/") || g_str_has_prefix (name, "image/")) { GstDiscovererVideoInfo *info; GstVideoFormat format; if (parent) info = (GstDiscovererVideoInfo *) parent; else { info = (GstDiscovererVideoInfo *) gst_mini_object_new (GST_TYPE_DISCOVERER_VIDEO_INFO); info->parent.caps = caps; } if (gst_video_format_parse_caps (caps, &format, &tmp, &tmp2)) { info->width = (guint) tmp; info->height = (guint) tmp2; } if (gst_structure_get_int (caps_st, "depth", &tmp)) info->depth = (guint) tmp; if (gst_video_parse_caps_pixel_aspect_ratio (caps, &tmp, &tmp2)) { info->par_num = tmp; info->par_denom = tmp2; } if (gst_video_parse_caps_framerate (caps, &tmp, &tmp2)) { info->framerate_num = tmp; info->framerate_denom = tmp2; } if (gst_video_format_parse_caps_interlaced (caps, &btmp)) info->interlaced = btmp; if (gst_structure_id_has_field (st, _TAGS_QUARK)) { gst_structure_id_get (st, _TAGS_QUARK, GST_TYPE_STRUCTURE, &tags_st, NULL); if (gst_structure_get_uint (tags_st, GST_TAG_BITRATE, &utmp) || gst_structure_get_uint (tags_st, GST_TAG_NOMINAL_BITRATE, &utmp)) info->bitrate = utmp; if (gst_structure_get_uint (tags_st, GST_TAG_MAXIMUM_BITRATE, &utmp)) info->max_bitrate = utmp; /* FIXME: Is it worth it to remove the tags we've parsed? */ info->parent.tags = gst_tag_list_merge (info->parent.tags, (GstTagList *) tags_st, GST_TAG_MERGE_REPLACE); gst_structure_free (tags_st); } return (GstDiscovererStreamInfo *) info; } else { /* None of the above - populate what information we can */ GstDiscovererStreamInfo *info; if (parent) info = parent; else { info = (GstDiscovererStreamInfo *) gst_mini_object_new (GST_TYPE_DISCOVERER_STREAM_INFO); info->caps = caps; } if (gst_structure_id_get (st, _TAGS_QUARK, GST_TYPE_STRUCTURE, &tags_st, NULL)) { info->tags = gst_tag_list_merge (info->tags, (GstTagList *) tags_st, GST_TAG_MERGE_REPLACE); gst_structure_free (tags_st); } return info; } }
static GstBuffer * gst_tag_lib_mux_render_tag (GstTagLibMux * mux) { GstTagLibMuxClass *klass; GstTagMergeMode merge_mode; GstTagSetter *tagsetter; GstBuffer *buffer; const GstTagList *tagsetter_tags; GstTagList *taglist; GstEvent *event; tagsetter = GST_TAG_SETTER (mux); tagsetter_tags = gst_tag_setter_get_tag_list (tagsetter); merge_mode = gst_tag_setter_get_tag_merge_mode (tagsetter); GST_LOG_OBJECT (mux, "merging tags, merge mode = %d", merge_mode); GST_LOG_OBJECT (mux, "event tags: %" GST_PTR_FORMAT, mux->event_tags); GST_LOG_OBJECT (mux, "set tags: %" GST_PTR_FORMAT, tagsetter_tags); taglist = gst_tag_list_merge (tagsetter_tags, mux->event_tags, merge_mode); GST_LOG_OBJECT (mux, "final tags: %" GST_PTR_FORMAT, taglist); klass = GST_TAG_LIB_MUX_CLASS (G_OBJECT_GET_CLASS (mux)); if (klass->render_tag == NULL) goto no_vfunc; buffer = klass->render_tag (mux, taglist); if (buffer == NULL) goto render_error; mux->tag_size = GST_BUFFER_SIZE (buffer); GST_LOG_OBJECT (mux, "tag size = %" G_GSIZE_FORMAT " bytes", mux->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 (mux->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 */ /* gst_event_new_tag takes ownership of the list, so no need to unref it */ event = gst_event_new_tag (taglist); gst_pad_push_event (mux->srcpad, event); GST_BUFFER_OFFSET (buffer) = 0; return buffer; no_vfunc: { GST_ERROR_OBJECT (mux, "Subclass does not implement render_tag vfunc!"); gst_tag_list_free (taglist); return NULL; } render_error: { GST_ERROR_OBJECT (mux, "Failed to render tag"); gst_tag_list_free (taglist); return NULL; } }
GstFlowReturn gst_kate_util_decoder_base_chain_kate_packet (GstKateDecoderBase * decoder, GstElement * element, GstPad * pad, GstBuffer * buf, GstPad * srcpad, const kate_event ** ev) { kate_packet kp; int ret; GstFlowReturn rflow = GST_FLOW_OK; GST_DEBUG_OBJECT (element, "got kate packet, %u bytes, type %02x", GST_BUFFER_SIZE (buf), GST_BUFFER_SIZE (buf) == 0 ? -1 : GST_BUFFER_DATA (buf)[0]); kate_packet_wrap (&kp, GST_BUFFER_SIZE (buf), GST_BUFFER_DATA (buf)); ret = kate_high_decode_packetin (&decoder->k, &kp, ev); if (G_UNLIKELY (ret < 0)) { GST_ELEMENT_ERROR (element, STREAM, DECODE, (NULL), ("Failed to decode Kate packet: %d", ret)); return GST_FLOW_ERROR; } else if (G_UNLIKELY (ret > 0)) { GST_DEBUG_OBJECT (element, "kate_high_decode_packetin has received EOS packet"); return GST_FLOW_OK; } /* headers may be interesting to retrieve information from */ if (G_LIKELY (GST_BUFFER_SIZE (buf) > 0)) switch (GST_BUFFER_DATA (buf)[0]) { GstCaps *caps; case 0x80: /* ID header */ GST_INFO_OBJECT (element, "Parsed ID header: language %s, category %s", decoder->k.ki->language, decoder->k.ki->category); caps = gst_caps_new_simple ("text/x-pango-markup", NULL); gst_pad_set_caps (srcpad, caps); gst_caps_unref (caps); if (decoder->k.ki->language && *decoder->k.ki->language) { GstTagList *old = decoder->tags, *tags = gst_tag_list_new (); if (tags) { gchar *lang_code; /* en_GB -> en */ lang_code = g_ascii_strdown (decoder->k.ki->language, -1); g_strdelimit (lang_code, NULL, '\0'); gst_tag_list_add (tags, GST_TAG_MERGE_APPEND, GST_TAG_LANGUAGE_CODE, lang_code, NULL); g_free (lang_code); /* TODO: category - where should it go ? */ decoder->tags = gst_tag_list_merge (decoder->tags, tags, GST_TAG_MERGE_REPLACE); gst_tag_list_free (tags); if (old) gst_tag_list_free (old); } } /* update properties */ if (decoder->language) g_free (decoder->language); decoder->language = g_strdup (decoder->k.ki->language); if (decoder->category) g_free (decoder->category); decoder->category = g_strdup (decoder->k.ki->category); decoder->original_canvas_width = decoder->k.ki->original_canvas_width; decoder->original_canvas_height = decoder->k.ki->original_canvas_height; break; case 0x81: /* Vorbis comments header */ GST_INFO_OBJECT (element, "Parsed comments header"); { gchar *encoder = NULL; GstTagList *old = decoder->tags, *list = gst_tag_list_from_vorbiscomment_buffer (buf, (const guint8 *) "\201kate\0\0\0\0", 9, &encoder); if (list) { decoder->tags = gst_tag_list_merge (decoder->tags, list, GST_TAG_MERGE_REPLACE); gst_tag_list_free (list); } if (!decoder->tags) { GST_ERROR_OBJECT (element, "failed to decode comment header"); decoder->tags = gst_tag_list_new (); } if (encoder) { gst_tag_list_add (decoder->tags, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, encoder, NULL); g_free (encoder); } gst_tag_list_add (decoder->tags, GST_TAG_MERGE_REPLACE, GST_TAG_SUBTITLE_CODEC, "Kate", NULL); gst_tag_list_add (decoder->tags, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER_VERSION, decoder->k.ki->bitstream_version_major, NULL); if (old) gst_tag_list_free (old); if (decoder->initialized) { gst_element_found_tags_for_pad (element, srcpad, decoder->tags); decoder->tags = NULL; } else { /* Only push them as messages for the time being. * * They will be pushed on the pad once the decoder is initialized */ gst_element_post_message (element, gst_message_new_tag (GST_OBJECT (element), gst_tag_list_copy (decoder->tags))); } } break; default: break; } return rflow; }
static GstFlowReturn vorbis_handle_comment_packet (GstVorbisDec * vd, ogg_packet * packet) { guint bitrate = 0; gchar *encoder = NULL; GstTagList *list, *old_list; GstBuffer *buf; GST_DEBUG_OBJECT (vd, "parsing comment packet"); buf = gst_buffer_new (); GST_BUFFER_DATA (buf) = gst_ogg_packet_data (packet); GST_BUFFER_SIZE (buf) = gst_ogg_packet_size (packet); list = gst_tag_list_from_vorbiscomment_buffer (buf, (guint8 *) "\003vorbis", 7, &encoder); old_list = vd->taglist; vd->taglist = gst_tag_list_merge (vd->taglist, list, GST_TAG_MERGE_REPLACE); if (old_list) gst_tag_list_free (old_list); gst_tag_list_free (list); gst_buffer_unref (buf); if (!vd->taglist) { GST_ERROR_OBJECT (vd, "couldn't decode comments"); vd->taglist = gst_tag_list_new (); } if (encoder) { if (encoder[0]) gst_tag_list_add (vd->taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, encoder, NULL); g_free (encoder); } gst_tag_list_add (vd->taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER_VERSION, vd->vi.version, GST_TAG_AUDIO_CODEC, "Vorbis", NULL); if (vd->vi.bitrate_nominal > 0 && vd->vi.bitrate_nominal <= 0x7FFFFFFF) { gst_tag_list_add (vd->taglist, GST_TAG_MERGE_REPLACE, GST_TAG_NOMINAL_BITRATE, (guint) vd->vi.bitrate_nominal, NULL); bitrate = vd->vi.bitrate_nominal; } if (vd->vi.bitrate_upper > 0 && vd->vi.bitrate_upper <= 0x7FFFFFFF) { gst_tag_list_add (vd->taglist, GST_TAG_MERGE_REPLACE, GST_TAG_MAXIMUM_BITRATE, (guint) vd->vi.bitrate_upper, NULL); if (!bitrate) bitrate = vd->vi.bitrate_upper; } if (vd->vi.bitrate_lower > 0 && vd->vi.bitrate_lower <= 0x7FFFFFFF) { gst_tag_list_add (vd->taglist, GST_TAG_MERGE_REPLACE, GST_TAG_MINIMUM_BITRATE, (guint) vd->vi.bitrate_lower, NULL); if (!bitrate) bitrate = vd->vi.bitrate_lower; } if (bitrate) { gst_tag_list_add (vd->taglist, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, (guint) bitrate, NULL); } if (vd->initialized) { gst_element_found_tags_for_pad (GST_ELEMENT_CAST (vd), vd->srcpad, vd->taglist); vd->taglist = NULL; } else { /* Only post them as messages for the time being. * * They will be pushed on the pad once the decoder is initialized */ gst_element_post_message (GST_ELEMENT_CAST (vd), gst_message_new_tag (GST_OBJECT (vd), gst_tag_list_copy (vd->taglist))); } return GST_FLOW_OK; }
/* Returns TRUE if processing should stop */ static gboolean handle_message (GstDiscoverer * dc, GstMessage * msg) { gboolean done = FALSE; GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg), "got a %s message", GST_MESSAGE_TYPE_NAME (msg)); switch (GST_MESSAGE_TYPE (msg)) { case GST_MESSAGE_ERROR:{ GError *gerr; gchar *debug; gst_message_parse_error (msg, &gerr, &debug); GST_WARNING_OBJECT (GST_MESSAGE_SRC (msg), "Got an error [debug:%s], [message:%s]", debug, gerr->message); dc->priv->current_error = gerr; g_free (debug); /* We need to stop */ done = TRUE; GST_DEBUG ("Setting result to ERROR"); dc->priv->current_info->result = GST_DISCOVERER_ERROR; } break; case GST_MESSAGE_EOS: GST_DEBUG ("Got EOS !"); done = TRUE; break; case GST_MESSAGE_ASYNC_DONE: if (GST_MESSAGE_SRC (msg) == (GstObject *) dc->priv->pipeline) { GST_DEBUG ("Finished changing state asynchronously"); done = TRUE; } break; case GST_MESSAGE_ELEMENT: { GQuark sttype = gst_structure_get_name_id (msg->structure); GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg), "structure %" GST_PTR_FORMAT, msg->structure); if (sttype == _MISSING_PLUGIN_QUARK) { GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg), "Setting result to MISSING_PLUGINS"); dc->priv->current_info->result = GST_DISCOVERER_MISSING_PLUGINS; dc->priv->current_info->misc = gst_structure_copy (msg->structure); } else if (sttype == _STREAM_TOPOLOGY_QUARK) { dc->priv->current_topology = gst_structure_copy (msg->structure); } } break; case GST_MESSAGE_TAG: { GstTagList *tl, *tmp; gst_message_parse_tag (msg, &tl); GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg), "Got tags %" GST_PTR_FORMAT, tl); /* Merge with current tags */ tmp = gst_tag_list_merge (dc->priv->current_info->tags, tl, GST_TAG_MERGE_APPEND); gst_tag_list_free (tl); if (dc->priv->current_info->tags) gst_tag_list_free (dc->priv->current_info->tags); dc->priv->current_info->tags = tmp; GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg), "Current info %p, tags %" GST_PTR_FORMAT, dc->priv->current_info, tmp); } break; default: break; } return done; }