[gst-devel] Question about gst_structure_get_name()
W. Michael Petullo
mike at flyn.org
Tue May 18 18:43:19 CEST 2010
I am using gst_structure_get_name() to determine the mimetype of a
stream (along with other things). In fact, I am doing this a few thousand
times, each time setting up and tearing down a pipeline that makes use of
decodebin. Because gst_structure_get_name() returns a constant string,
I thought that it was wrong to free the returned string. However, I am
finding that not free'ing the returned string results in what appears
to be a memory leak. Approximately 5-6MB of memory is consumed
after processing 2,419 media files. Free'ing the string returned by
gst_structure_get_name() results in this memory not being consumed.
Can someone comment on the correct use of gst_structure_get_name()? Or
do these symptoms indicate a problem elsewhere? I have attached my C
source for reference.
--
Mike
:wq
-------------- next part --------------
/*
* Utility functions implemented using GStreamer
*
* Copyright (C) 2009 W. Michael Petullo <mike at flyn.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <string.h>
#include <gst/gst.h>
#include "dmapd-daap-record.h"
#include "av-meta-reader-gst.h"
static GOnce once = G_ONCE_INIT;
struct AVMetaReaderGstPrivate {
GMutex *tag_read;
GstElement *pipeline;
GstElement *src;
GstElement *decode;
GstElement *typefind;
gboolean has_video;
};
static void
av_meta_reader_gst_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
static void
av_meta_reader_gst_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
static GOptionGroup *
av_meta_reader_gst_get_option_group (AVMetaReader *reader)
{
return gst_init_get_option_group ();
}
static void
av_meta_reader_gst_finalize (AVMetaReaderGst *self)
{
//G_OBJECT_CLASS (av_meta_reader_gst_parent_class)->finalize (object);
}
static void
av_meta_reader_gst_class_finalize (AVMetaReaderGstClass *klass)
{
}
static gboolean
message_loop (GstElement *element, GstTagList **tags)
{
GstBus *bus;
gboolean done = FALSE;
bus = gst_element_get_bus (element);
g_return_val_if_fail (bus != NULL, FALSE);
g_return_val_if_fail (tags != NULL, FALSE);
while (! done) {
GstMessage *message;
message = gst_bus_pop (bus);
if (message == NULL)
/* All messages read, we're done */
break;
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_TAG: {
GstTagList *new_tags, *old_tags;
gst_message_parse_tag (message, &new_tags);
if (*tags) {
old_tags = *tags;
*tags = gst_tag_list_merge (old_tags,
new_tags,
GST_TAG_MERGE_KEEP);
gst_tag_list_free (old_tags);
gst_tag_list_free (new_tags);
} else
*tags = new_tags;
break;
}
default:
break;
}
gst_message_unref (message);
}
gst_object_unref (bus);
return TRUE;
}
static gchar *
determine_format (DAAPRecord *record, const gchar *description)
{
gchar *format;
if (g_strrstr (description, "MP3"))
format = "mp3";
else if (g_strrstr (description, "MPEG-4 AAC"))
format = "aac";
else if (g_strrstr (description, "Vorbis"))
format = "ogg";
else if (g_strrstr (description, "FLAC"))
format = "flac";
else {
gchar *ext, *location;
g_debug ("Could not determine type from stream, using filename extension");
g_object_get (record, "location", &location, NULL);
ext = strrchr (location, '.');
if (ext == NULL) {
ext = "mp3";
} else {
ext++;
}
format = ext;
}
g_debug ("Format is %s", format);
return format;
}
static void
insert_tag (const GstTagList * list, const gchar * tag, DAAPRecord *record)
{
gint i, count;
if (tag == NULL)
return;
count = gst_tag_list_get_tag_size (list, tag);
for (i = 0; i < count; i++) {
gchar *val;
if (gst_tag_get_type (tag) == G_TYPE_STRING) {
if (!gst_tag_list_get_string_index (list, tag, i, &val))
g_assert_not_reached ();
} else {
val = g_strdup_value_contents (gst_tag_list_get_value_index (list, tag, i));
}
g_debug ("%s is %s", tag, val);
if (! strcmp ("title", tag)) {
g_object_set (record, "title", val, NULL);
} else if (! strcmp ("artist", tag)) {
g_object_set (record, "artist", val, NULL);
} else if (! strcmp ("album", tag)) {
g_object_set (record, "album", val, NULL);
} else if (! strcmp ("genre", tag)) {
g_object_set (record, "genre", val, NULL);
} else if (! strcmp ("audio-codec", tag)) {
gboolean has_video;
g_debug ("%s video", has_video ? "Has" : "Does not have");
g_object_set (record, "real-format", determine_format (record, val), NULL);
/* Determine the format to "advertise" (i.e., used for
* URL path)
*/
g_object_get (record, "has-video", &has_video, NULL);
if (has_video) {
/* FIXME: get from video stream. */
gchar *ext, *location;
g_object_get (record, "location", &location, NULL);
ext = strrchr (location, '.');
if (ext == NULL) {
ext = "mov";
} else {
ext++;
}
g_object_set (record, "format", ext, NULL);
} else {
g_object_set (record, "format", determine_format (record, val), NULL);
}
} else if (! strcmp ("track-number", tag)) {
g_object_set (record, "track", atoi (val), NULL);
} else {
/* Unused. */
g_debug ("Unused metadata");
}
g_free (val);
}
}
static void
setup_mutex (gpointer user_data)
{
AV_META_READER_GST (user_data)->priv->tag_read = g_mutex_new ();
}
static gboolean
setup_pipeline (AVMetaReaderGst *reader)
{
/* Set up pipeline. */
reader->priv->pipeline = gst_pipeline_new ("pipeline");
g_debug ("Make filesrc");
reader->priv->src = gst_element_factory_make ("filesrc", "source");
if (reader->priv->src == NULL) {
g_warning ("Error creating filesrc element");
return FALSE;
}
g_debug ("Make decodebin");
reader->priv->decode = gst_element_factory_make ("decodebin", "decoder");
if (reader->priv->decode == NULL) {
g_warning ("Error creating decodebin element");
return FALSE;
}
g_debug ("Make typefind");
reader->priv->typefind = gst_element_factory_make (
"typefind", "typefind");
if (reader->priv->typefind == NULL) {
g_warning ("Error creating typefind element");
return FALSE;
}
gst_bin_add_many (GST_BIN (reader->priv->pipeline),
reader->priv->src,
reader->priv->decode,
reader->priv->typefind,
NULL);
if (gst_element_link (reader->priv->src, reader->priv->decode) == FALSE) {
g_warning ("Error linking pipeline");
return FALSE;
}
g_debug ("Pipeline complete");
return TRUE;
}
static gboolean
pause_pipeline (GstElement *pipeline)
{
GstState state;
GstStateChangeReturn sret;
sret = gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PAUSED);
if (GST_STATE_CHANGE_ASYNC == sret) {
if (GST_STATE_CHANGE_SUCCESS != gst_element_get_state (GST_ELEMENT (pipeline), &state, NULL, 1 * GST_SECOND)) {
g_warning ("State change failed");
}
} else if (sret != GST_STATE_CHANGE_SUCCESS) {
g_print ("Could not read file");
}
/* Run once. */
return FALSE;
}
static gboolean
quit_mainloop (GMainLoop *loop)
{
g_main_loop_quit (loop);
/* Run once. */
return FALSE;
}
static void
no_more_pads_cb (GstElement *element, GMainLoop *loop)
{
quit_mainloop (loop);
}
/* FIXME: copied from libdmapsharing: */
gboolean
pads_compatible (GstPad *pad1, GstPad *pad2)
{
gboolean fnval;
GstCaps *res, *caps1, *caps2;
caps1 = gst_pad_get_caps (pad1);
caps2 = gst_pad_get_caps (pad2);
res = gst_caps_intersect (caps1, caps2);
fnval = res && ! gst_caps_is_empty (res);
gst_caps_unref (res);
gst_caps_unref (caps2);
gst_caps_unref (caps1);
return fnval;
}
static void
newpad_cb (GstElement *decodebin, GstPad *pad, gboolean last, AVMetaReaderGstPrivate *priv)
{
GstCaps *caps;
const gchar *mimetype;
GstStructure *structure;
/* Get mimetype. */
caps = gst_pad_get_caps (pad);
if (gst_caps_is_empty (caps) || gst_caps_is_any (caps)) {
g_warning ("Error getting caps from pad");
goto _return;
}
structure = gst_caps_get_structure (caps, 0);
mimetype = gst_structure_get_name (structure);
g_debug ("Pad mimetype is %s.", mimetype);
if (g_strrstr (mimetype, "video")) {
priv->has_video |= TRUE;
} else if (g_strrstr (mimetype, "audio")) {
GstPad *typefind_pad;
typefind_pad = gst_element_get_static_pad (priv->typefind, "sink");
g_assert (typefind_pad != NULL);
if (pads_compatible (pad, typefind_pad)) {
g_assert (! GST_PAD_IS_LINKED (
gst_element_get_static_pad (priv->typefind, "sink")));
gst_pad_link (pad, typefind_pad);
}
gst_object_unref (typefind_pad);
}
/* FIXME: mimetype is a const. However, if I do not free it, dmapd
* seems to leak memory (~5MB with my music library).
*/
g_free (mimetype);
_return:
gst_caps_unref (caps);
}
static gboolean
av_meta_reader_gst_read (AVMetaReader *reader, DAAPRecord *record, const gchar *path)
{
GstFormat fmt;
GstTagList *tags = NULL;
gint64 nanoduration;
gboolean has_video = FALSE;
GMainLoop *loop;
AVMetaReaderGst *gst_reader = AV_META_READER_GST (reader);
g_once (&once, (GThreadFunc) setup_mutex, gst_reader);
loop = g_main_loop_new (NULL, FALSE);
g_mutex_lock (gst_reader->priv->tag_read);
if (! setup_pipeline (gst_reader))
goto _return;
g_object_set (G_OBJECT (gst_reader->priv->src), "location", path, NULL);
/* Connect callback to identify audio and/or video tracks. */
g_signal_connect (gst_reader->priv->decode,
"new-decoded-pad",
G_CALLBACK (newpad_cb),
gst_reader->priv);
g_signal_connect (gst_reader->priv->decode,
"no-more-pads",
G_CALLBACK (no_more_pads_cb),
loop);
/* Run main loop to allow decodebin to create pads. Quit on
* "no-more-pads" signal. */
g_idle_add ((GSourceFunc) pause_pipeline, gst_reader->priv->pipeline);
g_timeout_add_seconds (1, (GSourceFunc) quit_mainloop, loop);
g_main_loop_run (loop);
fmt = GST_FORMAT_TIME;
if (! gst_element_query_duration (gst_reader->priv->typefind,
&fmt,
&nanoduration)) {
g_warning ("Could not determine duration of %s", path);
} else {
/* NOTE: cast avoids segfault on MIPS32: */
g_object_set (record, "duration", (gint32) (nanoduration / GST_SECOND), NULL);
}
if (!message_loop (GST_ELEMENT (gst_reader->priv->pipeline), &tags)) {
g_warning ("Failed in message reading for %s\n", path);
}
/* NOTE: Must set has_video before calling insert_tag. */
g_object_set (record, "has-video", has_video, NULL);
if (tags) {
gst_tag_list_foreach (tags, (GstTagForeachFunc) insert_tag, record);
gst_tag_list_free (tags);
tags = NULL;
} else
g_warning ("No metadata found for %s\n", path);
_return:
gst_element_set_state (gst_reader->priv->pipeline, GST_STATE_NULL);
g_object_unref (gst_reader->priv->pipeline);
g_mutex_unlock (gst_reader->priv->tag_read);
// g_main_loop_unref (loop);
return TRUE;
}
static void av_meta_reader_gst_register_type (GTypeModule *module);
G_MODULE_EXPORT gboolean
dmapd_module_load (GTypeModule *module)
{
av_meta_reader_gst_register_type (module);
}
G_MODULE_EXPORT gboolean
dmapd_module_unload (GTypeModule *module)
{
}
static void av_meta_reader_gst_init (AVMetaReaderGst *reader)
{
reader->priv = AV_META_READER_GST_GET_PRIVATE (reader);
}
static void av_meta_reader_gst_class_init (AVMetaReaderGstClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
AVMetaReaderClass *av_meta_reader_class = AV_META_READER_CLASS (klass);
g_type_class_add_private (klass, sizeof (AVMetaReaderGstPrivate));
gobject_class->set_property = av_meta_reader_gst_set_property;
gobject_class->get_property = av_meta_reader_gst_get_property;
gobject_class->finalize = av_meta_reader_gst_finalize;
av_meta_reader_class->read = av_meta_reader_gst_read;
av_meta_reader_class->get_option_group = av_meta_reader_gst_get_option_group;
}
G_DEFINE_DYNAMIC_TYPE (AVMetaReaderGst,
av_meta_reader_gst,
TYPE_AV_META_READER)
More information about the gstreamer-devel
mailing list