~ubuntu-branches/ubuntu/quantal/rhythmbox/quantal

« back to all changes in this revision

Viewing changes to .pc/0002-metadata-reset-tags-properly-in-rb_metadata_reset-bu.patch/metadata/rb-metadata-gst.c

  • Committer: Package Import Robot
  • Author(s): Michael Biebl
  • Date: 2012-06-10 16:19:59 UTC
  • mfrom: (1.3.7)
  • mto: This revision was merged to the branch mainline in revision 215.
  • Revision ID: package-import@ubuntu.com-20120610161959-x1tju93su2c0yv7c
Tags: 2.97-1
* New upstream release.
  - New more compact header layout, including album art.
    Closes: #660483, #658717
* debian/watch: Also track odd version numbers since rhythmbox does not seem
  to follow the typical GNOME versioning scheme.
* Remove patches which have been applied upstream.
* Update librhythmbox-core5 → librhythmbox-core6 for the soname bump.
* Add explicit Build-Depends on libxml2-dev (>= 2.7.8).
* Drop artdisplay plugin. It has been removed upstream as it is no longer
  needed now that the playing track display includes album art.
* Drop Build-Depends on python-gst0.10-dev and replace Depends on
  python-gst0.10 with gir1.2-gstreamer-0.10 (required by the replaygain
  plugin).
* Drop Recommends on nautilus-sendto. The sendto plugin is for sending
  selected tracks by email or instant message from within rhythmbox. It is
  not a nautilus-sendto plugin.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2
 
 *
3
 
 *  Copyright (C) 2010 Jonathan Matthew  <jonathan@d14n.org>
4
 
 *
5
 
 *  This program is free software; you can redistribute it and/or modify
6
 
 *  it under the terms of the GNU General Public License as published by
7
 
 *  the Free Software Foundation; either version 2 of the License, or
8
 
 *  (at your option) any later version.
9
 
 *
10
 
 *  The Rhythmbox authors hereby grant permission for non-GPL compatible
11
 
 *  GStreamer plugins to be used and distributed together with GStreamer
12
 
 *  and Rhythmbox. This permission is above and beyond the permissions granted
13
 
 *  by the GPL license by which Rhythmbox is covered. If you modify this code
14
 
 *  you may extend this exception to your version of the code, but you are not
15
 
 *  obligated to do so. If you do not wish to do so, delete this exception
16
 
 *  statement from your version.
17
 
 *
18
 
 *  This program is distributed in the hope that it will be useful,
19
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 
 *  GNU General Public License for more details.
22
 
 *
23
 
 *  You should have received a copy of the GNU General Public License
24
 
 *  along with this program; if not, write to the Free Software
25
 
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
26
 
 *
27
 
 */
28
 
 
29
 
#include <config.h>
30
 
 
31
 
#include <glib/gi18n.h>
32
 
#include <gio/gio.h>
33
 
#include <gst/gst.h>
34
 
#include <gst/pbutils/gstdiscoverer.h>
35
 
#include <gst/pbutils/pbutils.h>
36
 
 
37
 
#include "rb-metadata.h"
38
 
#include "rb-metadata-gst-common.h"
39
 
#include "rb-gst-media-types.h"
40
 
#include "rb-debug.h"
41
 
#include "rb-file-helpers.h"
42
 
 
43
 
/* copied from gstplay-enum.h */
44
 
typedef enum {
45
 
  GST_AUTOPLUG_SELECT_TRY,
46
 
  GST_AUTOPLUG_SELECT_EXPOSE,
47
 
  GST_AUTOPLUG_SELECT_SKIP
48
 
} GstAutoplugSelectResult;
49
 
 
50
 
typedef GstElement *(*RBAddTaggerElem) (GstElement *pipeline, GstPad *srcpad, GstTagList *tags);
51
 
 
52
 
static gboolean check_gst_plugin_version (const char *plugin, const char *element, gint major, gint minor, gint micro);
53
 
G_DEFINE_TYPE(RBMetaData, rb_metadata, G_TYPE_OBJECT)
54
 
 
55
 
struct RBMetaDataPrivate
56
 
{
57
 
        GstDiscovererInfo *info;
58
 
 
59
 
        char *mediatype;
60
 
        gboolean has_audio;
61
 
        gboolean has_non_audio;
62
 
        gboolean has_video;
63
 
        guint audio_bitrate;
64
 
        GstCaps *jpeg_image_caps;
65
 
 
66
 
        /* writing */
67
 
        GstElement *pipeline;
68
 
        GstElement *sink;
69
 
        GHashTable *taggers;
70
 
        GstTagList *tags;
71
 
        gboolean sink_linked;
72
 
};
73
 
 
74
 
void
75
 
rb_metadata_reset (RBMetaData *md)
76
 
{
77
 
        if (md->priv->tags != NULL) {
78
 
                g_object_unref (md->priv->tags);
79
 
        }
80
 
 
81
 
        if (md->priv->info != NULL) {
82
 
                gst_discoverer_info_unref (md->priv->info);
83
 
                md->priv->info = NULL;
84
 
        }
85
 
        g_free (md->priv->mediatype);
86
 
        md->priv->mediatype = NULL;
87
 
 
88
 
        md->priv->has_audio = FALSE;
89
 
        md->priv->has_non_audio = FALSE;
90
 
        md->priv->has_video = FALSE;
91
 
}
92
 
 
93
 
static void
94
 
have_type_cb (GstElement *element, guint probability, GstCaps *caps, RBMetaData *md)
95
 
{
96
 
        md->priv->mediatype = rb_gst_caps_to_media_type (caps);
97
 
        rb_debug ("got type %s", md->priv->mediatype);
98
 
}
99
 
 
100
 
static void
101
 
run_typefind (RBMetaData *md, const char *uri)
102
 
{
103
 
        GstElement *src;
104
 
 
105
 
        src = gst_element_make_from_uri (GST_URI_SRC, uri, NULL);
106
 
        if (src != NULL) {
107
 
                GstElement *pipeline = gst_pipeline_new (NULL);
108
 
                GstElement *sink = gst_element_factory_make ("fakesink", NULL);
109
 
                GstElement *typefind = gst_element_factory_make ("typefind", NULL);
110
 
 
111
 
                gst_bin_add_many (GST_BIN (pipeline), src, typefind, sink, NULL);
112
 
                if (gst_element_link_many (src, typefind, sink, NULL)) {
113
 
                        GstBus *bus;
114
 
                        GstMessage *message;
115
 
                        gboolean done;
116
 
 
117
 
                        g_signal_connect (typefind, "have-type", G_CALLBACK (have_type_cb), md);
118
 
 
119
 
                        bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
120
 
                        gst_element_set_state (pipeline, GST_STATE_PAUSED);
121
 
                        done = FALSE;
122
 
 
123
 
                        while (done == FALSE && md->priv->mediatype == NULL) {
124
 
                                message = gst_bus_timed_pop (bus, 5 * GST_SECOND);
125
 
                                if (message == NULL) {
126
 
                                        rb_debug ("typefind pass timed out");
127
 
                                        break;
128
 
                                }
129
 
 
130
 
                                switch (GST_MESSAGE_TYPE (message)) {
131
 
                                case GST_MESSAGE_ERROR:
132
 
                                        rb_debug ("typefind pass got an error");
133
 
                                        done = TRUE;
134
 
                                        break;
135
 
 
136
 
                                case GST_MESSAGE_STATE_CHANGED:
137
 
                                        if (GST_MESSAGE_SRC (message) == GST_OBJECT (pipeline)) {
138
 
                                                GstState old, new, pending;
139
 
                                                gst_message_parse_state_changed (message, &old, &new, &pending);
140
 
                                                if (new == GST_STATE_PAUSED && pending == GST_STATE_VOID_PENDING) {
141
 
                                                        rb_debug ("typefind pipeline reached PAUSED");
142
 
                                                        done = TRUE;
143
 
                                                }
144
 
                                        }
145
 
                                        break;
146
 
 
147
 
                                default:
148
 
                                        break;
149
 
                                }
150
 
                        }
151
 
 
152
 
                        gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
153
 
                }
154
 
 
155
 
                g_object_unref (pipeline);
156
 
        }
157
 
}
158
 
 
159
 
void
160
 
rb_metadata_load (RBMetaData *md, const char *uri, GError **error)
161
 
{
162
 
        GList *streams;
163
 
        GList *l;
164
 
        GstDiscoverer *discoverer;
165
 
        GstCaps *caps;
166
 
 
167
 
        rb_metadata_reset (md);
168
 
 
169
 
        discoverer = gst_discoverer_new (30 * GST_SECOND, NULL);
170
 
        md->priv->info = gst_discoverer_discover_uri (discoverer, g_strdup (uri), error);
171
 
        g_object_unref (discoverer);
172
 
 
173
 
        /* figure out if we've got audio, non-audio, or video streams */
174
 
        streams = gst_discoverer_info_get_streams (md->priv->info, GST_TYPE_DISCOVERER_STREAM_INFO);
175
 
        for (l = streams; l != NULL; l = l->next) {
176
 
                GstDiscovererStreamInfo *s = (GstDiscovererStreamInfo *)l->data;
177
 
                const char *mediatype;
178
 
                caps = gst_discoverer_stream_info_get_caps (s);
179
 
                mediatype = gst_structure_get_name (gst_caps_get_structure (caps, 0));
180
 
 
181
 
                if (GST_IS_DISCOVERER_AUDIO_INFO (s)) {
182
 
                        md->priv->has_audio = TRUE;
183
 
 
184
 
                        md->priv->audio_bitrate = gst_discoverer_audio_info_get_bitrate (GST_DISCOVERER_AUDIO_INFO (s));
185
 
 
186
 
                        g_free (md->priv->mediatype);
187
 
                        md->priv->mediatype = rb_gst_caps_to_media_type (caps);
188
 
                        rb_debug ("found audio stream, media type %s", md->priv->mediatype);
189
 
                } else if (GST_IS_DISCOVERER_CONTAINER_INFO (s)) {
190
 
                        if (md->priv->mediatype == NULL) {
191
 
                                md->priv->mediatype = g_strdup (mediatype);
192
 
                                rb_debug ("found container, media type %s", md->priv->mediatype);
193
 
                        } else {
194
 
                                rb_debug ("found container, ignoring media type");
195
 
                        }
196
 
                } else if (g_strcmp0 (mediatype, "application/x-id3") == 0 ||
197
 
                           g_strcmp0 (mediatype, "application/x-apetag") == 0) {
198
 
                        rb_debug ("found tag type, ignoring");
199
 
                } else {
200
 
                        if (GST_IS_DISCOVERER_VIDEO_INFO (s)) {
201
 
                                /* pretend low-framerate jpeg isn't video */
202
 
                                if (gst_caps_can_intersect (caps, md->priv->jpeg_image_caps)) {
203
 
                                        rb_debug ("found a jpeg image stream, not actual video");
204
 
                                } else {
205
 
                                        md->priv->has_video = TRUE;
206
 
                                }
207
 
                        }
208
 
                        md->priv->has_non_audio = TRUE;
209
 
                        if (md->priv->mediatype == NULL) {
210
 
                                md->priv->mediatype = g_strdup (mediatype);
211
 
                                rb_debug ("found video/image/other stream, media type %s", md->priv->mediatype);
212
 
                        } else {
213
 
                                rb_debug ("found video/image/other stream, ignoring media type (%s)", mediatype);
214
 
                        }
215
 
                        rb_debug ("video caps: %s", gst_caps_to_string (caps));
216
 
                }
217
 
 
218
 
                gst_caps_unref (caps);
219
 
        }
220
 
        gst_discoverer_stream_info_list_free (streams);
221
 
 
222
 
        /* if we don't have a media type, use typefind to get one */
223
 
        if (md->priv->mediatype == NULL) {
224
 
                run_typefind (md, uri);
225
 
        }
226
 
 
227
 
        /* look at missing plugin information too */
228
 
        switch (rb_gst_get_missing_plugin_type (gst_discoverer_info_get_misc (md->priv->info))) {
229
 
        case MEDIA_TYPE_NONE:
230
 
                break;
231
 
        case MEDIA_TYPE_CONTAINER:
232
 
                /* hm, maybe we need a way to say 'we don't even know what's in here'.
233
 
                 * but for now, the things we actually identify as containers are mostly
234
 
                 * used for audio, so pretending they actually are is good enough.
235
 
                 */
236
 
        case MEDIA_TYPE_AUDIO:
237
 
                md->priv->has_audio = TRUE;
238
 
                break;
239
 
        case MEDIA_TYPE_VIDEO:
240
 
                md->priv->has_video = TRUE;
241
 
                break;
242
 
        case MEDIA_TYPE_OTHER:
243
 
                md->priv->has_non_audio = TRUE;
244
 
                break;
245
 
        default:
246
 
                g_assert_not_reached ();
247
 
        }
248
 
}
249
 
 
250
 
gboolean
251
 
rb_metadata_has_missing_plugins (RBMetaData *md)
252
 
{
253
 
        const GstStructure *s;
254
 
        if (md->priv->info == NULL) {
255
 
                return FALSE;
256
 
        }
257
 
 
258
 
        s = gst_discoverer_info_get_misc (md->priv->info);
259
 
        return (rb_gst_get_missing_plugin_type (s) != MEDIA_TYPE_NONE);
260
 
}
261
 
 
262
 
gboolean
263
 
rb_metadata_get_missing_plugins (RBMetaData *md, char ***missing_plugins, char ***plugin_descriptions)
264
 
{
265
 
        char **mp;
266
 
        char **pd;
267
 
        GstMessage *msg;
268
 
        const GstStructure *misc;
269
 
 
270
 
        if (rb_metadata_has_missing_plugins (md) == FALSE) {
271
 
                return FALSE;
272
 
        }
273
 
 
274
 
        mp = g_new0 (char *, 2);
275
 
        pd = g_new0 (char *, 2);
276
 
 
277
 
        /* wrap the structure in a new message so we can use the
278
 
         * pbutils functions to look at it.
279
 
         */
280
 
        misc = gst_discoverer_info_get_misc (md->priv->info);
281
 
        msg = gst_message_new_element (NULL, gst_structure_copy (misc));
282
 
        mp[0] = gst_missing_plugin_message_get_installer_detail (msg);
283
 
        pd[0] = gst_missing_plugin_message_get_description (msg);
284
 
        gst_message_unref (msg);
285
 
 
286
 
        *missing_plugins = mp;
287
 
        *plugin_descriptions = pd;
288
 
        return TRUE;
289
 
}
290
 
 
291
 
gboolean
292
 
rb_metadata_has_audio (RBMetaData *md)
293
 
{
294
 
        return md->priv->has_audio;
295
 
}
296
 
 
297
 
gboolean
298
 
rb_metadata_has_video (RBMetaData *md)
299
 
{
300
 
        return md->priv->has_video;
301
 
}
302
 
 
303
 
gboolean
304
 
rb_metadata_has_other_data (RBMetaData *md)
305
 
{
306
 
        return md->priv->has_non_audio;
307
 
}
308
 
 
309
 
gboolean
310
 
rb_metadata_can_save (RBMetaData *md, const char *mediatype)
311
 
{
312
 
        return FALSE;
313
 
}
314
 
 
315
 
char **
316
 
rb_metadata_get_saveable_types (RBMetaData *md)
317
 
{
318
 
        char **types;
319
 
        GList *taggers;
320
 
        GList *t;
321
 
        int i;
322
 
 
323
 
        taggers = g_hash_table_get_keys (md->priv->taggers);
324
 
        types = g_new0 (char *, g_list_length (taggers) + 1);
325
 
        i = 0;
326
 
        for (t = taggers; t != NULL; t = t->next) {
327
 
                types[i++] = g_strdup (t->data);
328
 
        }
329
 
 
330
 
        g_list_free (taggers);
331
 
        return types;
332
 
}
333
 
 
334
 
static gboolean
335
 
check_gst_plugin_version (const char *plugin, const char *element, int major, int minor, int micro)
336
 
{
337
 
        const char *version;
338
 
        GstPlugin *p;
339
 
        guint i;
340
 
        guint count;
341
 
 
342
 
        if (gst_default_registry_check_feature_version (element, major, minor, micro + 1))
343
 
                return TRUE;
344
 
 
345
 
        if (!gst_default_registry_check_feature_version (element, major, minor, micro))
346
 
                return FALSE;
347
 
 
348
 
        p = gst_default_registry_find_plugin (plugin);
349
 
        if (p == NULL)
350
 
                return FALSE;
351
 
 
352
 
        version = gst_plugin_get_version (p);
353
 
 
354
 
        /* check if it's not a release */       /* what */
355
 
        count = sscanf (version, "%u.%u.%u.%u", &i, &i, &i, &i);
356
 
        return (count > 3);
357
 
}
358
 
 
359
 
static gboolean
360
 
link_named_pad (GstPad *srcpad, GstElement *element, const char *sinkpadname)
361
 
{
362
 
        GstPad *sinkpad;
363
 
        GstPadLinkReturn result;
364
 
 
365
 
        sinkpad = gst_element_get_static_pad (element, sinkpadname);
366
 
        if (sinkpad == NULL) {
367
 
                sinkpad = gst_element_get_request_pad (element, sinkpadname);
368
 
        }
369
 
        result = gst_pad_link (srcpad, sinkpad);
370
 
        gst_object_unref (sinkpad);
371
 
 
372
 
        if (GST_PAD_LINK_SUCCESSFUL (result)) {
373
 
                return TRUE;
374
 
        } else {
375
 
                char *srcname = gst_pad_get_name (srcpad);
376
 
                char *sinkname = gst_pad_get_name (sinkpad);
377
 
                rb_debug ("couldn't link %s to %s: %d",
378
 
                          srcname, sinkname, result);
379
 
                return FALSE;
380
 
        }
381
 
}
382
 
 
383
 
static GstElement *
384
 
flac_tagger (GstElement *pipeline, GstPad *srcpad, GstTagList *tags)
385
 
{
386
 
        GstElement *tagger = NULL;
387
 
 
388
 
        tagger = gst_element_factory_make ("flactag", NULL);
389
 
        if (tagger == NULL)
390
 
                return NULL;
391
 
 
392
 
        gst_bin_add (GST_BIN (pipeline), tagger);
393
 
        if (!link_named_pad (srcpad, tagger, "sink"))
394
 
                goto error;
395
 
 
396
 
        gst_element_set_state (tagger, GST_STATE_PAUSED);
397
 
        if (tags != NULL) {
398
 
                gst_tag_setter_merge_tags (GST_TAG_SETTER (tagger), tags, GST_TAG_MERGE_REPLACE_ALL);
399
 
        }
400
 
        return tagger;
401
 
 
402
 
error:
403
 
        gst_object_unref (tagger);
404
 
        return NULL;
405
 
}
406
 
 
407
 
static GstElement *
408
 
mp3_tagger (GstElement *pipeline, GstPad *srcpad, GstTagList *tags)
409
 
{
410
 
        GstElement *mux = NULL;
411
 
 
412
 
        /* try id3mux first, since it's more supported and writes id3v2.3 tags rather than v2.4.  */
413
 
        if (check_gst_plugin_version ("id3mux", "id3mux", 0, 10, 12)) {
414
 
                mux = gst_element_factory_make ("id3mux", NULL);
415
 
        }
416
 
 
417
 
        if (mux == NULL)
418
 
                mux = gst_element_factory_make ("id3v2mux", NULL);
419
 
 
420
 
        if (mux == NULL)
421
 
                goto error;
422
 
 
423
 
        gst_bin_add (GST_BIN (pipeline), mux);
424
 
        if (!link_named_pad (srcpad, mux, "sink")) {
425
 
                rb_debug ("couldn't link decoded pad to id3 muxer");
426
 
                goto error;
427
 
        }
428
 
 
429
 
        gst_element_set_state (mux, GST_STATE_PAUSED);
430
 
        if (tags != NULL) {
431
 
                gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), tags, GST_TAG_MERGE_REPLACE_ALL);
432
 
        }
433
 
        rb_debug ("id3 tagger created");
434
 
        return mux;
435
 
 
436
 
error:
437
 
        if (mux != NULL) {
438
 
                g_object_unref (mux);
439
 
        }
440
 
        return NULL;
441
 
}
442
 
 
443
 
static GstElement *
444
 
vorbis_tagger (GstElement *pipeline, GstPad *srcpad, GstTagList *tags)
445
 
{
446
 
        GstElement *mux;
447
 
        GstElement *tagger;
448
 
        GstElement *parser;
449
 
 
450
 
        mux = gst_element_factory_make ("oggmux", NULL);
451
 
        parser = gst_element_factory_make ("vorbisparse", NULL);
452
 
        tagger = gst_element_factory_make ("vorbistag", NULL);
453
 
        if (mux == NULL || parser == NULL || tagger == NULL)
454
 
                goto error;
455
 
 
456
 
        gst_bin_add_many (GST_BIN (pipeline), parser, tagger, mux, NULL);
457
 
        if (!link_named_pad (srcpad, parser, "sink"))
458
 
                goto error;
459
 
        if (!gst_element_link_many (parser, tagger, mux, NULL))
460
 
                goto error;
461
 
 
462
 
        gst_element_set_state (parser, GST_STATE_PAUSED);
463
 
        gst_element_set_state (tagger, GST_STATE_PAUSED);
464
 
        gst_element_set_state (mux, GST_STATE_PAUSED);
465
 
        if (tags != NULL) {
466
 
                gst_tag_setter_merge_tags (GST_TAG_SETTER (tagger), tags, GST_TAG_MERGE_REPLACE_ALL);
467
 
        }
468
 
        return mux;
469
 
 
470
 
error:
471
 
        if (parser != NULL)
472
 
                g_object_unref (parser);
473
 
        if (tagger != NULL)
474
 
                g_object_unref (tagger);
475
 
        if (mux != NULL)
476
 
                g_object_unref (mux);
477
 
        return NULL;
478
 
}
479
 
 
480
 
static GstElement *
481
 
mp4_tagger (GstElement *pipeline, GstPad *srcpad, GstTagList *tags)
482
 
{
483
 
        GstElement *mux;
484
 
 
485
 
        mux = gst_element_factory_make ("mp4mux", NULL);
486
 
        if (mux == NULL)
487
 
                return NULL;
488
 
 
489
 
        gst_bin_add (GST_BIN (pipeline), mux);
490
 
        if (!link_named_pad (srcpad, mux, "audio_%d"))
491
 
                goto error;
492
 
 
493
 
        gst_element_set_state (mux, GST_STATE_PAUSED);
494
 
        if (tags != NULL) {
495
 
                gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), tags, GST_TAG_MERGE_REPLACE_ALL);
496
 
        }
497
 
        return mux;
498
 
 
499
 
error:
500
 
        g_object_unref (mux);
501
 
        return NULL;
502
 
}
503
 
 
504
 
static void
505
 
metadata_save_new_decoded_pad_cb (GstElement *decodebin, GstPad *pad, gboolean last, RBMetaData *md)
506
 
{
507
 
        RBAddTaggerElem add_tagger_func = NULL;
508
 
        GstElement *retag_end;
509
 
        GstCaps *caps;
510
 
        char *caps_str;
511
 
        GHashTableIter iter;
512
 
        gpointer key;
513
 
        gpointer value;
514
 
 
515
 
        if (md->priv->sink_linked) {
516
 
                GError *error;
517
 
                error = g_error_new (GST_STREAM_ERROR,
518
 
                                     GST_STREAM_ERROR_FORMAT,
519
 
                                     _("Unable to write tags to this file as it contains multiple streams"));
520
 
                gst_element_post_message (decodebin,
521
 
                                          gst_message_new_error (GST_OBJECT (decodebin),
522
 
                                                                 error,
523
 
                                                                 NULL));
524
 
                g_error_free (error);
525
 
                return;
526
 
        }
527
 
 
528
 
        /* find a tagger function that accepts the caps */
529
 
        caps = gst_pad_get_caps (pad);
530
 
        caps_str = gst_caps_to_string (caps);
531
 
        rb_debug ("finding tagger for src caps %s", caps_str);
532
 
        g_free (caps_str);
533
 
 
534
 
        g_hash_table_iter_init (&iter, md->priv->taggers);
535
 
        while (g_hash_table_iter_next (&iter, &key, &value)) {
536
 
                GstCaps *tagger_caps;
537
 
                const char *media_type = (const char *)key;
538
 
 
539
 
                tagger_caps = rb_gst_media_type_to_caps (media_type);
540
 
                /* not sure this is right, really */
541
 
                if (gst_caps_is_always_compatible (caps, tagger_caps)) {
542
 
                        caps_str = gst_caps_to_string (tagger_caps);
543
 
                        rb_debug ("matched sink caps %s", caps_str);
544
 
                        g_free (caps_str);
545
 
 
546
 
                        gst_caps_unref (tagger_caps);
547
 
                        add_tagger_func = (RBAddTaggerElem)value;
548
 
                        break;
549
 
                }
550
 
                gst_caps_unref (tagger_caps);
551
 
        }
552
 
        gst_caps_unref (caps);
553
 
 
554
 
        /* add retagging element(s) */
555
 
        if (add_tagger_func == NULL) {
556
 
                GError *error;
557
 
                error = g_error_new (GST_STREAM_ERROR,
558
 
                                     GST_STREAM_ERROR_FORMAT,
559
 
                                     _("Unable to write tags to this file as it is not encoded in a supported format"));
560
 
                gst_element_post_message (decodebin,
561
 
                                          gst_message_new_error (GST_OBJECT (decodebin),
562
 
                                                                 error,
563
 
                                                                 NULL));
564
 
                g_error_free (error);
565
 
                return;
566
 
        }
567
 
        retag_end = add_tagger_func (md->priv->pipeline, pad, md->priv->tags);
568
 
 
569
 
        /* link to the sink */
570
 
        gst_element_link (retag_end, md->priv->sink);
571
 
        md->priv->sink_linked = TRUE;
572
 
}
573
 
 
574
 
static gboolean
575
 
factory_src_caps_intersect (GstElementFactory *factory, GstCaps *caps)
576
 
{
577
 
        const GList *templates;
578
 
        const GList *l;
579
 
 
580
 
        templates = gst_element_factory_get_static_pad_templates (factory);
581
 
        for (l = templates; l != NULL; l = l->next) {
582
 
                GstStaticPadTemplate *t = l->data;
583
 
                GstCaps *tcaps;
584
 
 
585
 
                if (t->direction != GST_PAD_SRC) {
586
 
                        continue;
587
 
                }
588
 
 
589
 
                tcaps = gst_static_pad_template_get_caps (t);
590
 
                if (gst_caps_can_intersect (tcaps, caps)) {
591
 
                        gst_caps_unref (tcaps);
592
 
                        return TRUE;
593
 
                }
594
 
                gst_caps_unref (tcaps);
595
 
        }
596
 
        return FALSE;
597
 
}
598
 
 
599
 
static GstAutoplugSelectResult
600
 
metadata_save_autoplug_select_cb (GstElement *decodebin, GstPad *pad, GstCaps *caps, GstElementFactory *factory, RBMetaData *md)
601
 
{
602
 
        GstCaps *src_caps;
603
 
        gboolean is_any;
604
 
        gboolean is_raw;
605
 
        gboolean is_demuxer;
606
 
 
607
 
        is_demuxer = (strstr (gst_element_factory_get_klass (factory), "Demuxer") != NULL);
608
 
        if (is_demuxer) {
609
 
                /* allow demuxers, since we're going to remux later */
610
 
                return GST_AUTOPLUG_SELECT_TRY;
611
 
        }
612
 
 
613
 
        src_caps = gst_caps_new_any ();
614
 
        is_any = gst_element_factory_can_src_caps (factory, src_caps);
615
 
        gst_caps_unref (src_caps);
616
 
        if (is_any) {
617
 
                /* this is something like id3demux (though that will match the
618
 
                 * above check), allow it so we can get to the actual decoder later
619
 
                 */
620
 
                return GST_AUTOPLUG_SELECT_TRY;
621
 
        }
622
 
 
623
 
        src_caps = gst_caps_from_string ("audio/x-raw-int; audio/x-raw-float; video/x-raw-yuv; video/x-raw-rgb; video/x-raw-gray");
624
 
        is_raw = factory_src_caps_intersect (factory, src_caps);
625
 
        gst_caps_unref (src_caps);
626
 
 
627
 
        if (is_raw == FALSE) {
628
 
                /* this is probably a parser or something, allow it */
629
 
                return GST_AUTOPLUG_SELECT_TRY;
630
 
        }
631
 
 
632
 
        /* don't allow decoders */
633
 
        return GST_AUTOPLUG_SELECT_EXPOSE;
634
 
}
635
 
 
636
 
static gboolean
637
 
check_file_valid (const char *original, const char *newfile)
638
 
{
639
 
        RBMetaData *md = rb_metadata_new ();
640
 
        GError *error = NULL;
641
 
        gboolean ret;
642
 
 
643
 
        rb_metadata_load (md, newfile, &error);
644
 
        ret = (error == NULL);
645
 
 
646
 
        /* TODO: check that the tags are correct? */
647
 
 
648
 
        if (error != NULL)
649
 
                g_error_free (error);
650
 
        g_object_unref (md);
651
 
        return ret;
652
 
}
653
 
 
654
 
 
655
 
void
656
 
rb_metadata_save (RBMetaData *md, const char *uri, GError **error)
657
 
{
658
 
        GstElement *pipeline = NULL;
659
 
        GstElement *urisrc = NULL;
660
 
        GstElement *decodebin = NULL;
661
 
        char *tmpname_prefix = NULL;
662
 
        char *tmpname = NULL;
663
 
        GOutputStream *stream = NULL;
664
 
        GError *io_error = NULL;
665
 
        GstBus *bus;
666
 
        gboolean done;
667
 
        GFile *src;
668
 
        GFile *dest;
669
 
 
670
 
        rb_debug ("saving metadata for uri: %s", uri);
671
 
 
672
 
        tmpname_prefix = rb_uri_make_hidden (uri);
673
 
        rb_debug ("temporary file name prefix: %s", tmpname_prefix);
674
 
 
675
 
        rb_uri_mkstemp (tmpname_prefix, &tmpname, &stream, &io_error);
676
 
        g_free (tmpname_prefix);
677
 
        if (io_error != NULL) {
678
 
                goto gio_error;
679
 
        }
680
 
 
681
 
        /* set up pipeline */
682
 
        pipeline = gst_pipeline_new ("pipeline");
683
 
        md->priv->pipeline = pipeline;
684
 
        md->priv->sink_linked = FALSE;
685
 
 
686
 
        urisrc = gst_element_make_from_uri (GST_URI_SRC, uri, "urisrc");
687
 
        if (urisrc == NULL) {
688
 
                g_set_error (error,
689
 
                             RB_METADATA_ERROR,
690
 
                             RB_METADATA_ERROR_MISSING_PLUGIN,
691
 
                             _("Failed to create a source element; check your installation"));
692
 
                rb_debug ("missing an element to load the uri, sadly");
693
 
                goto out;
694
 
        }
695
 
 
696
 
        decodebin = gst_element_factory_make ("decodebin2", "decoder");
697
 
        if (decodebin == NULL) {
698
 
                g_set_error (error,
699
 
                             RB_METADATA_ERROR,
700
 
                             RB_METADATA_ERROR_MISSING_PLUGIN,
701
 
                             _("Failed to create the 'decodebin2' element; check your GStreamer installation"));
702
 
                goto out;
703
 
        }
704
 
 
705
 
        md->priv->sink = gst_element_factory_make ("giostreamsink", "sink");
706
 
        if (md->priv->sink == NULL) {
707
 
                g_set_error (error,
708
 
                             RB_METADATA_ERROR,
709
 
                             RB_METADATA_ERROR_MISSING_PLUGIN,
710
 
                             _("Failed to create the 'giostreamsink' element; check your GStreamer installation"));
711
 
                goto out;
712
 
        }
713
 
        g_object_set (md->priv->sink, "stream", stream, NULL);
714
 
 
715
 
        gst_bin_add_many (GST_BIN (pipeline), urisrc, decodebin, md->priv->sink, NULL);
716
 
        gst_element_link (urisrc, decodebin);
717
 
 
718
 
        g_signal_connect_object (decodebin,
719
 
                                 "new-decoded-pad",
720
 
                                 G_CALLBACK (metadata_save_new_decoded_pad_cb),
721
 
                                 md, 0);
722
 
        g_signal_connect_object (decodebin,
723
 
                                 "autoplug-select",
724
 
                                 G_CALLBACK (metadata_save_autoplug_select_cb),
725
 
                                 md, 0);
726
 
 
727
 
        /* run pipeline .. */
728
 
        gst_element_set_state (pipeline, GST_STATE_PLAYING);
729
 
        bus = gst_element_get_bus (pipeline);
730
 
        done = FALSE;
731
 
        while (done == FALSE) {
732
 
                GstMessage *message;
733
 
 
734
 
                message = gst_bus_timed_pop (bus, GST_CLOCK_TIME_NONE);
735
 
                if (message == NULL) {
736
 
                        /* can this even happen? */
737
 
                        rb_debug ("breaking out of bus polling loop");
738
 
                        break;
739
 
                }
740
 
 
741
 
                switch (GST_MESSAGE_TYPE (message)) {
742
 
                case GST_MESSAGE_ERROR:
743
 
                        {
744
 
                                GError *gerror;
745
 
                                char *debug;
746
 
 
747
 
                                gst_message_parse_error (message, &gerror, &debug);
748
 
                                if (*error != NULL) {
749
 
                                        rb_debug ("caught error: %s (%s), but we've already got one", gerror->message, debug);
750
 
                                } else {
751
 
                                        rb_debug ("caught error: %s (%s)", gerror->message, debug);
752
 
 
753
 
                                        g_clear_error (error);
754
 
                                        *error = g_error_new_literal (RB_METADATA_ERROR,
755
 
                                                                      RB_METADATA_ERROR_GENERAL,
756
 
                                                                      gerror->message);
757
 
                                }
758
 
                                done = TRUE;
759
 
 
760
 
                                g_error_free (gerror);
761
 
                                g_free (debug);
762
 
                        }
763
 
                        break;
764
 
 
765
 
                case GST_MESSAGE_EOS:
766
 
                        rb_debug ("got eos message");
767
 
                        done = TRUE;
768
 
                        break;
769
 
 
770
 
                default:
771
 
                        break;
772
 
                }
773
 
 
774
 
                gst_message_unref (message);
775
 
        }
776
 
        gst_object_unref (bus);
777
 
        gst_element_set_state (pipeline, GST_STATE_NULL);
778
 
 
779
 
        if (g_output_stream_close (stream, NULL, &io_error) == FALSE) {
780
 
                goto gio_error;
781
 
        }
782
 
        g_object_unref (stream);
783
 
        stream = NULL;
784
 
 
785
 
        if (*error == NULL) {
786
 
                /* check to ensure the file isn't corrupt */
787
 
                if (!check_file_valid (uri, tmpname)) {
788
 
                        g_set_error (error,
789
 
                                     RB_METADATA_ERROR,
790
 
                                     RB_METADATA_ERROR_INTERNAL,
791
 
                                     _("File corrupted during write"));
792
 
                        goto out_error;
793
 
                }
794
 
 
795
 
                src = g_file_new_for_uri (tmpname);
796
 
                dest = g_file_new_for_uri (uri);
797
 
 
798
 
                /* try to copy attributes over, not likely to help much though */
799
 
                g_file_copy_attributes (dest, src, G_FILE_COPY_ALL_METADATA, NULL, NULL);
800
 
 
801
 
                g_file_move (src, dest, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &io_error);
802
 
                if (io_error != NULL) {
803
 
                        goto gio_error;
804
 
                }
805
 
                goto out;
806
 
        }
807
 
 
808
 
gio_error:
809
 
        if (*error == NULL) {
810
 
                g_set_error (error,
811
 
                             RB_METADATA_ERROR,
812
 
                             RB_METADATA_ERROR_IO,
813
 
                             "%s",
814
 
                             io_error->message);
815
 
        }
816
 
out_error:
817
 
        if (tmpname != NULL) {
818
 
                /* clean up temporary file */
819
 
                rb_debug ("removing temporary file %s", tmpname);
820
 
                dest = g_file_new_for_uri (tmpname);
821
 
                if (g_file_delete (dest, NULL, NULL) == FALSE) {
822
 
                        rb_debug ("unable to remove temporary file?");
823
 
                }
824
 
                g_object_unref (dest);
825
 
        }
826
 
out:
827
 
        g_free (tmpname);
828
 
        if (pipeline != NULL)
829
 
                gst_object_unref (GST_OBJECT (pipeline));
830
 
}
831
 
 
832
 
gboolean
833
 
rb_metadata_get (RBMetaData *md, RBMetaDataField field, GValue *ret)
834
 
{
835
 
        const GstTagList *tags;
836
 
        const char *tag;
837
 
        GValue gstvalue = {0, };
838
 
        GstClockTime duration;
839
 
 
840
 
        /* special cases: mostly duration */
841
 
        switch (field) {
842
 
        case RB_METADATA_FIELD_DURATION:
843
 
                duration = gst_discoverer_info_get_duration (md->priv->info);
844
 
                if (duration != 0) {
845
 
                        g_value_init (ret, G_TYPE_ULONG);
846
 
                        g_value_set_ulong (ret, duration / (1000 * 1000 * 1000));
847
 
                        return TRUE;
848
 
                } else {
849
 
                        rb_debug ("no duration available");
850
 
                        return FALSE;
851
 
                }
852
 
                break;
853
 
        case RB_METADATA_FIELD_BITRATE:
854
 
                if (md->priv->audio_bitrate != 0) {
855
 
                        g_value_init (ret, G_TYPE_ULONG);
856
 
                        g_value_set_ulong (ret, md->priv->audio_bitrate / 1000);
857
 
                        return TRUE;
858
 
                } else {
859
 
                        return FALSE;
860
 
                }
861
 
        default:
862
 
                break;
863
 
        }
864
 
 
865
 
        tags = gst_discoverer_info_get_tags (md->priv->info);
866
 
        if (tags == NULL) {
867
 
                return FALSE;
868
 
        }
869
 
 
870
 
        tag = rb_metadata_gst_field_to_gst_tag (field);
871
 
        if (tag == NULL) {
872
 
                return FALSE;
873
 
        }
874
 
 
875
 
 
876
 
        if (rb_metadata_get_field_type (field) == G_TYPE_STRING) {
877
 
                char *str = NULL;
878
 
                const char *v;
879
 
                int i;
880
 
 
881
 
                /* pick the first valid utf8 string, or if we find a later
882
 
                 * string of which the first is a prefix, pick that.
883
 
                 */
884
 
                i = 0;
885
 
                while (gst_tag_list_peek_string_index (tags, tag, i, &v)) {
886
 
                        if (g_utf8_validate (v, -1, NULL) && (str == NULL || g_str_has_prefix (v, str))) {
887
 
                                g_free (str);
888
 
                                str = g_strdup (v);
889
 
                        } else {
890
 
                                rb_debug ("ignoring %s", v);
891
 
                        }
892
 
                        i++;
893
 
                }
894
 
 
895
 
                if (str != NULL) {
896
 
                        str = g_strstrip (str);
897
 
                        g_value_init (ret, G_TYPE_STRING);
898
 
                        g_value_take_string (ret, str);
899
 
                        return TRUE;
900
 
                } else {
901
 
                        return FALSE;
902
 
                }
903
 
        } else {
904
 
                if (gst_tag_list_copy_value (&gstvalue, tags, tag) == FALSE) {
905
 
                        return FALSE;
906
 
                }
907
 
                g_value_init (ret, rb_metadata_get_field_type (field));
908
 
                g_value_transform (&gstvalue, ret);
909
 
                g_value_unset (&gstvalue);
910
 
                return TRUE;
911
 
        }
912
 
}
913
 
 
914
 
const char *
915
 
rb_metadata_get_media_type (RBMetaData *md)
916
 
{
917
 
        return md->priv->mediatype;
918
 
}
919
 
 
920
 
gboolean
921
 
rb_metadata_set (RBMetaData *md, RBMetaDataField field, const GValue *val)
922
 
{
923
 
        /* don't write this out */
924
 
        if (field == RB_METADATA_FIELD_DURATION)
925
 
                return TRUE;
926
 
 
927
 
        if (field == RB_METADATA_FIELD_DATE && g_value_get_ulong (val) == 0) {
928
 
                /* we should ask gstreamer to remove the tag,
929
 
                 * but there is no easy way of doing so
930
 
                 */
931
 
        } else {
932
 
                const char *tag;
933
 
                GValue newval = {0,};
934
 
 
935
 
                tag = rb_metadata_gst_field_to_gst_tag (field);
936
 
                g_value_init (&newval, gst_tag_get_type (tag));
937
 
                if (g_value_transform (val, &newval)) {
938
 
                        rb_debug ("Setting %s",tag);
939
 
 
940
 
                        if (md->priv->tags == NULL) {
941
 
                                md->priv->tags = gst_tag_list_new ();
942
 
                        }
943
 
 
944
 
                        gst_tag_list_add_values (md->priv->tags,
945
 
                                                 GST_TAG_MERGE_APPEND,
946
 
                                                 tag, &newval,
947
 
                                                 NULL);
948
 
                }
949
 
                g_value_unset (&newval);
950
 
        }
951
 
 
952
 
        return TRUE;
953
 
}
954
 
 
955
 
static void
956
 
rb_metadata_finalize (GObject *object)
957
 
{
958
 
        RBMetaData *md;
959
 
        md = RB_METADATA (object);
960
 
        rb_metadata_reset (md);
961
 
 
962
 
        G_OBJECT_CLASS (rb_metadata_parent_class)->finalize (object);
963
 
}
964
 
 
965
 
static void
966
 
rb_metadata_init (RBMetaData *md)
967
 
{
968
 
        md->priv = (G_TYPE_INSTANCE_GET_PRIVATE ((md), RB_TYPE_METADATA, RBMetaDataPrivate));
969
 
 
970
 
        md->priv->taggers = g_hash_table_new (g_str_hash, g_str_equal);
971
 
 
972
 
        if (gst_element_factory_find ("giostreamsink") == FALSE) {
973
 
                rb_debug ("giostreamsink not found, can't tag anything");
974
 
        } else {
975
 
                if (gst_element_factory_find ("vorbistag") &&
976
 
                    gst_element_factory_find ("vorbisparse") &&
977
 
                    gst_element_factory_find ("oggmux")) {
978
 
                        rb_debug ("ogg vorbis tagging available");
979
 
                        g_hash_table_insert (md->priv->taggers, "audio/x-vorbis", (gpointer)vorbis_tagger);
980
 
                }
981
 
 
982
 
                if (gst_element_factory_find ("flactag")) {
983
 
                        rb_debug ("flac tagging available");
984
 
                        g_hash_table_insert (md->priv->taggers, "audio/x-flac", flac_tagger);
985
 
                }
986
 
 
987
 
                /* id3mux > 0.10.12 is the new one, not the broken old one. */
988
 
                if (gst_element_factory_find ("id3v2mux") || check_gst_plugin_version ("id3mux", "id3mux", 0, 10, 12)) {
989
 
                        rb_debug ("id3 tagging available");
990
 
                        g_hash_table_insert (md->priv->taggers, "audio/mpeg", mp3_tagger);
991
 
                }
992
 
 
993
 
                if (gst_element_factory_find ("mp4mux")) {
994
 
                        rb_debug ("mp4 tagging available");
995
 
                        g_hash_table_insert (md->priv->taggers, "audio/x-aac", mp4_tagger);
996
 
                }
997
 
        }
998
 
 
999
 
        md->priv->jpeg_image_caps = gst_caps_from_string ("image/jpeg, framerate = (fraction) [ 0, 1/1 ]");
1000
 
}
1001
 
 
1002
 
static void
1003
 
rb_metadata_class_init (RBMetaDataClass *klass)
1004
 
{
1005
 
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
1006
 
 
1007
 
        object_class->finalize = rb_metadata_finalize;
1008
 
 
1009
 
        g_type_class_add_private (klass, sizeof (RBMetaDataPrivate));
1010
 
        rb_metadata_gst_register_transforms ();
1011
 
}
1012
 
 
1013
 
RBMetaData *
1014
 
rb_metadata_new (void)
1015
 
{
1016
 
        return RB_METADATA (g_object_new (RB_TYPE_METADATA, NULL, NULL));
1017
 
}