aboutsummaryrefslogtreecommitdiff
path: root/docs/manual/advanced-metadata.xml
blob: 6a25e19f747df938674df3641e7b78b19641f59e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
<chapter id="chapter-metadata">
  <title>Metadata</title>

  <para>
    &GStreamer; makes a clear distinction between two types of metadata, and
    has support for both types. The first is stream tags, which describe the
    content of a stream in a non-technical way. Examples include the author
    of a song, the title of that very same song or the album it is a part of.
    The other type of metadata is stream-info, which is a somewhat technical
    description of the properties of a stream. This can include video size,
    audio samplerate, codecs used and so on. Tags are handled using the
    &GStreamer; tagging system. Stream-info can be retrieved from a
    <classname>GstPad</classname>.
  </para>

  <sect1 id="section-tags-read">
    <title>Metadata reading</title>

    <para>
      Stream information can most easily be read by reading it from a
      <classname>GstPad</classname>. This has already been discussed before
      in <xref linkend="section-caps-metadata"/>. Therefore, we will skip
      it here. Note that this requires access to all pads of which you
      want stream information.
    </para>

    <para>
      Tag reading is done through a bus in &GStreamer;, which has been
      discussed previously in <xref linkend="chapter-bus"/>. You can
      listen for <classname>GST_MESSAGE_TAG</classname> messages and handle
      them as you wish.
    </para>
    <para>
      Note, however, that the <classname>GST_MESSAGE_TAG</classname>
      message may be fired multiple times in the pipeline. It is the
      application's responsibility to put all those tags together and
      display them to the user in a nice, coherent way. Usually, using
      <function>gst_tag_list_merge ()</function> is a good enough way
      of doing this; make sure to empty the cache when loading a new song,
      or after every few minutes when listening to internet radio. Also,
      make sure you use <classname>GST_TAG_MERGE_PREPEND</classname> as
      merging mode, so that a new title (which came in later) has a
      preference over the old one for display.
    </para>
    <para>
      The following example will extract tags from a file and print them:
    </para>
    <programlisting>
/* compile with:
 * gcc -o tags tags.c `pkg-config --cflags --libs gstreamer-0.10` */
#include &lt;gst/gst.h&gt;

static void
print_one_tag (const GstTagList * list, const gchar * tag, gpointer user_data)
{
  int i, num;

  num = gst_tag_list_get_tag_size (list, tag);
  for (i = 0; i &lt; num; ++i) {
    const GValue *val;

    /* Note: when looking for specific tags, use the g_tag_list_get_xyz() API,
     * we only use the GValue approach here because it is more generic */
    val = gst_tag_list_get_value_index (list, tag, i);
    if (G_VALUE_HOLDS_STRING (val)) {
      g_print ("\t%20s : %s\n", tag, g_value_get_string (val));
    } else if (G_VALUE_HOLDS_UINT (val)) {
      g_print ("\t%20s : %u\n", tag, g_value_get_uint (val));
    } else if (G_VALUE_HOLDS_DOUBLE (val)) {
      g_print ("\t%20s : %g\n", tag, g_value_get_double (val));
    } else if (G_VALUE_HOLDS_BOOLEAN (val)) {
      g_print ("\t%20s : %s\n", tag,
          (g_value_get_boolean (val)) ? "true" : "false");
    } else if (GST_VALUE_HOLDS_BUFFER (val)) {
      g_print ("\t%20s : buffer of size %u\n", tag,
          GST_BUFFER_SIZE (gst_value_get_buffer (val)));
    } else if (GST_VALUE_HOLDS_DATE (val)) {
      g_print ("\t%20s : date (year=%u,...)\n", tag,
          g_date_get_year (gst_value_get_date (val)));
    } else {
      g_print ("\t%20s : tag of type '%s'\n", tag, G_VALUE_TYPE_NAME (val));
    }
  }
}

static void
on_new_pad (GstElement * dec, GstPad * pad, GstElement * fakesink)
{
  GstPad *sinkpad;

  sinkpad = gst_element_get_static_pad (fakesink, "sink");
  if (!gst_pad_is_linked (sinkpad)) {
    if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK)
      g_error ("Failed to link pads!");
  }
  gst_object_unref (sinkpad);
}

int
main (int argc, char ** argv)
{
  GstElement *pipe, *dec, *sink;
  GstMessage *msg;

  gst_init (&amp;argc, &amp;argv);

  if (argc &lt; 2 || !gst_uri_is_valid (argv[1]))
    g_error ("Usage: %s file:///path/to/file", argv[0]);

  pipe = gst_pipeline_new ("pipeline");

  dec = gst_element_factory_make ("uridecodebin", NULL);
  g_object_set (dec, "uri", argv[1], NULL);
  gst_bin_add (GST_BIN (pipe), dec);

  sink = gst_element_factory_make ("fakesink", NULL);
  gst_bin_add (GST_BIN (pipe), sink);

  g_signal_connect (dec, "pad-added", G_CALLBACK (on_new_pad), sink);

  gst_element_set_state (pipe, GST_STATE_PAUSED);

  while (TRUE) {
    GstTagList *tags = NULL;

    msg = gst_bus_timed_pop_filtered (GST_ELEMENT_BUS (pipe),
        GST_CLOCK_TIME_NONE,
        GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_TAG | GST_MESSAGE_ERROR);

    if (GST_MESSAGE_TYPE (msg) != GST_MESSAGE_TAG) /* error or async_done */
      break;

    gst_message_parse_tag (msg, &amp;tags);

    g_print ("Got tags from element %s:\n", GST_OBJECT_NAME (msg-&gt;src));
    gst_tag_list_foreach (tags, print_one_tag, NULL);
    g_print ("\n");
    gst_tag_list_unref (tags);

    gst_message_unref (msg);
  };

  if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR)
    g_error ("Got error");

  gst_message_unref (msg);
  gst_element_set_state (pipe, GST_STATE_NULL);
  gst_object_unref (pipe);
  return 0;
}
    </programlisting>
  </sect1>

  <sect1 id="section-tags-write">
    <title>Tag writing</title>

    <para>
      Tag writing is done using the <ulink type="http"
	url="&URLAPI;GstTagSetter.html"><classname>GstTagSetter</classname></ulink>
      interface. All that's required is a tag-set-supporting element in
      your pipeline. In order to see if any of the elements in your
      pipeline supports tag writing, you can use the function
      <function>gst_bin_iterate_all_by_interface (pipeline,
      GST_TYPE_TAG_SETTER)</function>. On the resulting element, usually
      an encoder or muxer, you can use <function>gst_tag_setter_merge
      ()</function> (with a taglist) or <function>gst_tag_setter_add
      ()</function> (with individual tags) to set tags on it.
    </para>
    <para>
      A nice extra feature in &GStreamer; tag support is that tags are
      preserved in pipelines. This means that if you transcode one file
      containing tags into another media type, and that new media type
      supports tags too, then the tags will be handled as part of the
      data stream and be merged into the newly written media file, too.
    </para>
  </sect1>
</chapter>