summaryrefslogtreecommitdiff
path: root/pwg-advanced-tagging.md
blob: 121e0e4b80b3b2db650addaac6944f0dd5137e43 (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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
---
title: Tagging (Metadata and Streaminfo)
...

# Tagging (Metadata and Streaminfo)

## Overview

Tags are pieces of information stored in a stream that are not the
content itself, but they rather *describe* the content. Most media
container formats support tagging in one way or another. Ogg uses
VorbisComment for this, MP3 uses ID3, AVI and WAV use RIFF's INFO list
chunk, etc. GStreamer provides a general way for elements to read tags
from the stream and expose this to the user. The tags (at least the
metadata) will be part of the stream inside the pipeline. The
consequence of this is that transcoding of files from one format to
another will automatically preserve tags, as long as the input and
output format elements both support tagging.

Tags are separated in two categories in GStreamer, even though
applications won't notice anything of this. The first are called
*metadata*, the second are called *streaminfo*. Metadata are tags that
describe the non-technical parts of stream content. They can be changed
without needing to re-encode the stream completely. Examples are
“author”, “title” or “album”. The container format might still need
to be re-written for the tags to fit in, though. Streaminfo, on the
other hand, are tags that describe the stream contents technically. To
change them, the stream needs to be re-encoded. Examples are “codec” or
“bitrate”. Note that some container formats (like ID3) store various
streaminfo tags as metadata in the file container, which means that they
can be changed so that they don't match the content in the file any
more. Still, they are called metadata because *technically*, they can be
changed without re-encoding the whole stream, even though that makes
them invalid. Files with such metadata tags will have the same tag
twice: once as metadata, once as streaminfo.

There is no special name for tag reading elements in GStreamer. There
are specialised elements (e.g. id3demux) that do nothing besides tag
reading, but any GStreamer element may extract tags while processing
data, and most decoders, demuxers and parsers do.

A tag writer is called
[`TagSetter`](../../gstreamer/html/GstTagSetter.html). An element
supporting both can be used in a tag editor for quick tag changing
(note: in-place tag editing is still poorly supported at the time of
writing and usually requires tag extraction/stripping and remuxing of
the stream with new tags).

## Reading Tags from Streams

The basic object for tags is a [`GstTagList
`](../../gstreamer/html/GstTagList.html). An element that is reading
tags from a stream should create an empty taglist and fill this with
individual tags. Empty tag lists can be created with `gst_tag_list_new
()`. Then, the element can fill the list using `gst_tag_list_add ()
` or `gst_tag_list_add_values ()`. Note that elements often read
metadata as strings, but the values in the taglist might not necessarily
be strings - they need to be of the type the tag was registered as (the
API documentation for each predefined tag should contain the type). Be
sure to use functions like `gst_value_transform ()` to make sure that
your data is of the right type. After data reading, you can send the
tags downstream with the TAG event. When the TAG event reaches the sink,
it will post the TAG message on the pipeline's GstBus for the
application to pick up.

We currently require the core to know the GType of tags before they are
being used, so all tags must be registered first. You can add new tags
to the list of known tags using `gst_tag_register ()`. If you think the
tag will be useful in more cases than just your own element, it might be
a good idea to add it to `gsttag.c` instead. That's up to you to decide.
If you want to do it in your own element, it's easiest to register the
tag in one of your class init functions, preferably `_class_init ()`.

``` c

static void
gst_my_filter_class_init (GstMyFilterClass *klass)
{
[..]
  gst_tag_register ("my_tag_name", GST_TAG_FLAG_META,
            G_TYPE_STRING,
            _("my own tag"),
            _("a tag that is specific to my own element"),
            NULL);
[..]
}

    
```

## Writing Tags to Streams

Tag writers are the opposite of tag readers. Tag writers only take
metadata tags into account, since that's the only type of tags that have
to be written into a stream. Tag writers can receive tags in three ways:
internal, application and pipeline. Internal tags are tags read by the
element itself, which means that the tag writer is - in that case - a
tag reader, too. Application tags are tags provided to the element via
the TagSetter interface (which is just a layer). Pipeline tags are tags
provided to the element from within the pipeline. The element receives
such tags via the `GST_EVENT_TAG` event, which means that tags writers
should implement an event handler. The tag writer is responsible for
combining all these three into one list and writing them to the output
stream.

The example below will receive tags from both application and pipeline,
combine them and write them to the output stream. It implements the tag
setter so applications can set tags, and retrieves pipeline tags from
incoming events.

Warning, this example is outdated and doesn't work with the 1.0 version
of GStreamer anymore.

``` c

GType
gst_my_filter_get_type (void)
{
[..]
    static const GInterfaceInfo tag_setter_info = {
      NULL,
      NULL,
      NULL
    };
[..]
    g_type_add_interface_static (my_filter_type,
                 GST_TYPE_TAG_SETTER,
                 &tag_setter_info);
[..]
}

static void
gst_my_filter_init (GstMyFilter *filter)
{
[..]
}

/*
 * Write one tag.
 */

static void
gst_my_filter_write_tag (const GstTagList *taglist,
             const gchar      *tagname,
             gpointer          data)
{
  GstMyFilter *filter = GST_MY_FILTER (data);
  GstBuffer *buffer;
  guint num_values = gst_tag_list_get_tag_size (list, tag_name), n;
  const GValue *from;
  GValue to = { 0 };

  g_value_init (&to, G_TYPE_STRING);

  for (n = 0; n < num_values; n++) {
    guint8 * data;
    gsize size;

    from = gst_tag_list_get_value_index (taglist, tagname, n);
    g_value_transform (from, &to);

    data = g_strdup_printf ("%s:%s", tagname,
        g_value_get_string (&to));
    size = strlen (data);

    buf = gst_buffer_new_wrapped (data, size);
    gst_pad_push (filter->srcpad, buf);
  }

  g_value_unset (&to);
}

static void
gst_my_filter_task_func (GstElement *element)
{
  GstMyFilter *filter = GST_MY_FILTER (element);
  GstTagSetter *tagsetter = GST_TAG_SETTER (element);
  GstData *data;
  GstEvent *event;
  gboolean eos = FALSE;
  GstTagList *taglist = gst_tag_list_new ();

  while (!eos) {
    data = gst_pad_pull (filter->sinkpad);

    /* We're not very much interested in data right now */
    if (GST_IS_BUFFER (data))
      gst_buffer_unref (GST_BUFFER (data));
    event = GST_EVENT (data);

    switch (GST_EVENT_TYPE (event)) {
      case GST_EVENT_TAG:
        gst_tag_list_insert (taglist, gst_event_tag_get_list (event),
                 GST_TAG_MERGE_PREPEND);
        gst_event_unref (event);
        break;
      case GST_EVENT_EOS:
        eos = TRUE;
        gst_event_unref (event);
        break;
      default:
        gst_pad_event_default (filter->sinkpad, event);
        break;
    }
  }

  /* merge tags with the ones retrieved from the application */
  if ((gst_tag_setter_get_tag_list (tagsetter)) {
    gst_tag_list_insert (taglist,
             gst_tag_setter_get_tag_list (tagsetter),
             gst_tag_setter_get_tag_merge_mode (tagsetter));
  }

  /* write tags */
  gst_tag_list_foreach (taglist, gst_my_filter_write_tag, filter);

  /* signal EOS */
  gst_pad_push (filter->srcpad, gst_event_new (GST_EVENT_EOS));
}

    
```

Note that normally, elements would not read the full stream before
processing tags. Rather, they would read from each sinkpad until they've
received data (since tags usually come in before the first data buffer)
and process that.