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